diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-08 03:08:39 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-08 03:08:39 +0000 |
commit | f77969819ac54a13966f16153cae23a66590cc35 (patch) | |
tree | 0dcd98d62f691240b94c4b908b19f4385c2ead23 | |
parent | 5ae54edfa9d6200eb12f831ef56a6e80207ca281 (diff) | |
download | gitlab-ce-f77969819ac54a13966f16153cae23a66590cc35.tar.gz |
Add latest changes from gitlab-org/gitlab@master
37 files changed, 560 insertions, 191 deletions
diff --git a/.rubocop.yml b/.rubocop.yml index 47dc85a79f0..12469c40b51 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -293,7 +293,7 @@ Gitlab/AvoidFeatureGet: Enabled: true RSpec/TimecopFreeze: - Enabled: false + Enabled: true AutoCorrect: true Include: - 'spec/**/*.rb' diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index d655f8beb95..ce5f53c9f84 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1173,3 +1173,96 @@ Rails/SaveBang: - 'spec/tasks/gitlab/web_hook_rake_spec.rb' - 'spec/uploaders/file_uploader_spec.rb' - 'spec/uploaders/object_storage_spec.rb' + +# Offense count: 187 +# Cop supports --auto-correct. +RSpec/TimecopFreeze: + Exclude: + - 'ee/spec/controllers/admin/application_settings_controller_spec.rb' + - 'ee/spec/controllers/projects/security/network_policies_controller_spec.rb' + - 'ee/spec/features/admin/admin_reset_pipeline_minutes_spec.rb' + - 'ee/spec/features/boards/sidebar_spec.rb' + - 'ee/spec/features/groups/analytics/cycle_analytics/filters_and_data_spec.rb' + - 'ee/spec/features/groups/iteration_spec.rb' + - 'ee/spec/features/projects/mirror_spec.rb' + - 'ee/spec/features/projects/services/prometheus_custom_metrics_spec.rb' + - 'ee/spec/finders/productivity_analytics_finder_spec.rb' + - 'ee/spec/frontend/fixtures/analytics.rb' + - 'ee/spec/helpers/vulnerabilities_helper_spec.rb' + - 'ee/spec/lib/analytics/merge_request_metrics_refresh_spec.rb' + - 'ee/spec/lib/analytics/productivity_analytics_request_params_spec.rb' + - 'ee/spec/lib/ee/gitlab/background_migration/populate_vulnerability_historical_statistics_spec.rb' + - 'ee/spec/lib/gitlab/analytics/cycle_analytics/data_collector_spec.rb' + - 'ee/spec/lib/gitlab/analytics/cycle_analytics/group_stage_time_summary_spec.rb' + - 'ee/spec/lib/gitlab/analytics/cycle_analytics/summary/group/stage_time_summary_spec.rb' + - 'ee/spec/lib/gitlab/analytics/type_of_work/tasks_by_type_spec.rb' + - 'ee/spec/lib/gitlab/auth/group_saml/sso_enforcer_spec.rb' + - 'ee/spec/lib/gitlab/database/load_balancing/host_spec.rb' + - 'ee/spec/lib/gitlab/geo/base_request_spec.rb' + - 'ee/spec/lib/gitlab/geo/event_gap_tracking_spec.rb' + - 'ee/spec/lib/gitlab/geo/git_push_http_spec.rb' + - 'ee/spec/lib/gitlab/geo/log_cursor/daemon_spec.rb' + - 'ee/spec/lib/gitlab/geo/log_cursor/events/repository_updated_event_spec.rb' + - 'ee/spec/lib/gitlab/geo/oauth/login_state_spec.rb' + - 'ee/spec/lib/gitlab/insights/reducers/count_per_period_reducer_spec.rb' + - 'ee/spec/lib/gitlab/prometheus/queries/additional_metrics_environment_query_spec.rb' + - 'ee/spec/lib/gitlab/prometheus/queries/cluster_query_spec.rb' + - 'ee/spec/migrations/populate_vulnerability_historical_statistics_for_year_spec.rb' + - 'ee/spec/migrations/remove_duplicated_cs_findings_spec.rb' + - 'ee/spec/migrations/remove_duplicated_cs_findings_without_vulnerability_id_spec.rb' + - 'ee/spec/migrations/schedule_fix_orphan_promoted_issues_spec.rb' + - 'ee/spec/migrations/schedule_merge_request_any_approval_rule_migration_spec.rb' + - 'ee/spec/migrations/schedule_populate_resolved_on_default_branch_column_spec.rb' + - 'ee/spec/migrations/schedule_populate_vulnerability_historical_statistics_spec.rb' + - 'ee/spec/migrations/schedule_project_any_approval_rule_migration_spec.rb' + - 'ee/spec/migrations/set_resolved_state_on_vulnerabilities_spec.rb' + - 'ee/spec/migrations/20190926180443_schedule_epic_issues_after_epics_move_spec.rb' + - 'ee/spec/models/analytics/cycle_analytics/group_level_spec.rb' + - 'ee/spec/models/burndown_spec.rb' + - 'ee/spec/models/ee/namespace_spec.rb' + - 'ee/spec/models/geo/project_registry_spec.rb' + - 'ee/spec/models/merge_train_spec.rb' + - 'ee/spec/models/productivity_analytics_spec.rb' + - 'ee/spec/models/project_spec.rb' + - 'ee/spec/models/vulnerabilities/export_spec.rb' + - 'ee/spec/requests/api/vulnerabilities_spec.rb' + - 'ee/spec/services/geo/file_download_service_spec.rb' + - 'ee/spec/services/vulnerabilities/confirm_service_spec.rb' + - 'ee/spec/services/vulnerabilities/dismiss_service_spec.rb' + - 'ee/spec/services/vulnerabilities/resolve_service_spec.rb' + - 'ee/spec/services/vulnerabilities/revert_to_detected_service_spec.rb' + - 'ee/spec/services/vulnerability_exports/export_service_spec.rb' + - 'ee/spec/support/shared_contexts/lib/gitlab/insights/reducers/reducers_shared_contexts.rb' + - 'qa/spec/support/repeater_spec.rb' + - 'spec/features/profiles/active_sessions_spec.rb' + - 'spec/features/projects/environments/environment_metrics_spec.rb' + - 'spec/features/users/active_sessions_spec.rb' + - 'spec/lib/atlassian/jira_connect/client_spec.rb' + - 'spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb' + - 'spec/lib/gitlab/analytics/cycle_analytics/median_spec.rb' + - 'spec/lib/gitlab/analytics/cycle_analytics/records_fetcher_spec.rb' + - 'spec/lib/gitlab/anonymous_session_spec.rb' + - 'spec/lib/gitlab/auth/unique_ips_limiter_spec.rb' + - 'spec/lib/gitlab/checks/timed_logger_spec.rb' + - 'spec/lib/gitlab/cycle_analytics/stage_summary_spec.rb' + - 'spec/lib/gitlab/cycle_analytics/usage_data_spec.rb' + - 'spec/lib/gitlab/instrumentation_helper_spec.rb' + - 'spec/lib/gitlab/omniauth_logging/json_formatter_spec.rb' + - 'spec/lib/gitlab/puma_logging/json_formatter_spec.rb' + - 'spec/lib/gitlab/sidekiq_logging/structured_logger_spec.rb' + - 'spec/lib/json_web_token/hmac_token_spec.rb' + - 'spec/lib/rspec_flaky/flaky_example_spec.rb' + - 'spec/lib/rspec_flaky/listener_spec.rb' + - 'spec/models/active_session_spec.rb' + - 'spec/models/container_repository_spec.rb' + - 'spec/models/pages/lookup_path_spec.rb' + - 'spec/models/project_feature_usage_spec.rb' + - 'spec/requests/api/v3/github_spec.rb' + - 'spec/serializers/entity_date_helper_spec.rb' + - 'spec/support/cycle_analytics_helpers/test_generation.rb' + - 'spec/support/helpers/cycle_analytics_helpers.rb' + - 'spec/support/helpers/javascript_fixtures_helpers.rb' + - 'spec/support/shared_contexts/rack_attack_shared_context.rb' + - 'spec/support/shared_examples/workers/concerns/reenqueuer_shared_examples.rb' + - 'spec/workers/concerns/reenqueuer_spec.rb' + - 'spec/workers/metrics/dashboard/prune_old_annotations_worker_spec.rb' diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js index 6ff74325a5e..2cd333f26e1 100644 --- a/app/assets/javascripts/pages/search/show/search.js +++ b/app/assets/javascripts/pages/search/show/search.js @@ -4,6 +4,7 @@ import { deprecatedCreateFlash as Flash } from '~/flash'; import Api from '~/api'; import { __ } from '~/locale'; import Project from '~/pages/projects/project'; +import { visitUrl } from '~/lib/utils/url_utility'; import refreshCounts from './refresh_counts'; import setHighlightClass from './highlight_blob_search_result'; @@ -86,6 +87,10 @@ export default class Search { $(document) .off('click', this.searchClear) .on('click', this.searchClear, this.clearSearchField.bind(this)); + + $('a.js-search-clear') + .off('click', this.clearSearchFilter) + .on('click', this.clearSearchFilter); } static submitSearch() { @@ -108,6 +113,17 @@ export default class Search { .focus(); } + // We need to manually follow the link on the anchors + // that have this event bound, as their `click` default + // behavior is prevented by the toggle logic. + /* eslint-disable-next-line class-methods-use-this */ + clearSearchFilter(ev) { + const $target = $(ev.currentTarget); + + visitUrl($target.href); + ev.stopPropagation(); + } + getProjectsData(term) { return new Promise(resolve => { if (this.groupId) { diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss index 86caed329b4..a62e28a9b8a 100644 --- a/app/assets/stylesheets/pages/search.scss +++ b/app/assets/stylesheets/pages/search.scss @@ -198,6 +198,15 @@ input[type='checkbox']:hover { } } + .search-clear { + position: absolute; + right: 10px; + top: 9px; + padding: 0; + line-height: 0; + background: none; + border: 0; + } .search-icon { position: absolute; @@ -247,14 +256,7 @@ input[type='checkbox']:hover { } .search-clear { - position: absolute; - right: 10px; - top: 9px; - padding: 0; color: $gray-darkest; - line-height: 0; - background: none; - border: 0; &:hover, &:focus { diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index 5edad410724..b5deed70380 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -48,6 +48,10 @@ class GraphqlController < ApplicationController render_error(exception.message, status: :unprocessable_entity) end + rescue_from ::GraphQL::CoercionError do |exception| + render_error(exception.message, status: :unprocessable_entity) + end + private def set_user_last_activity diff --git a/app/graphql/mutations/award_emojis/base.rb b/app/graphql/mutations/award_emojis/base.rb index 583744c3884..df6b883529e 100644 --- a/app/graphql/mutations/award_emojis/base.rb +++ b/app/graphql/mutations/award_emojis/base.rb @@ -6,7 +6,7 @@ module Mutations authorize :award_emoji argument :awardable_id, - GraphQL::ID_TYPE, + ::Types::GlobalIDType[::Awardable], required: true, description: 'The global id of the awardable resource' @@ -23,7 +23,10 @@ module Mutations private def find_object(id:) - GitlabSchema.object_from_id(id) + # TODO: remove this line when the compatibility layer is removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + id = ::Types::GlobalIDType[::Awardable].coerce_isolated_input(id) + GitlabSchema.find_by_gid(id) end # Called by mutations methods after performing an authorization check diff --git a/app/graphql/mutations/design_management/move.rb b/app/graphql/mutations/design_management/move.rb index 43e2e542408..aed4cfec0fd 100644 --- a/app/graphql/mutations/design_management/move.rb +++ b/app/graphql/mutations/design_management/move.rb @@ -38,7 +38,7 @@ module Mutations # TODO: remove this line when the compatibility layer is removed # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 id = DesignID.coerce_isolated_input(id) - GitlabSchema.object_from_id(id) + GitlabSchema.find_by_gid(id) end def not_found(gid) diff --git a/app/graphql/mutations/metrics/dashboard/annotations/create.rb b/app/graphql/mutations/metrics/dashboard/annotations/create.rb index 6f316e76e2a..b064f55825f 100644 --- a/app/graphql/mutations/metrics/dashboard/annotations/create.rb +++ b/app/graphql/mutations/metrics/dashboard/annotations/create.rb @@ -18,12 +18,12 @@ module Mutations description: 'The created annotation' argument :environment_id, - GraphQL::ID_TYPE, + ::Types::GlobalIDType[::Environment], required: false, description: 'The global id of the environment to add an annotation to' argument :cluster_id, - GraphQL::ID_TYPE, + ::Types::GlobalIDType[::Clusters::Cluster], required: false, description: 'The global id of the cluster to add an annotation to' @@ -84,7 +84,7 @@ module Mutations end def find_object(id:) - GitlabSchema.object_from_id(id) + GitlabSchema.find_by_gid(id) end def annotation_create_params(args) @@ -96,7 +96,16 @@ module Mutations end def annotation_source(args) - annotation_source_id = args[:cluster_id] || args[:environment_id] + # TODO: remove these lines when the compatibility layer is removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + annotation_source_id = if args[:cluster_id] + ::Types::GlobalIDType[::Clusters::Cluster].coerce_isolated_input(args[:cluster_id]) + else + ::Types::GlobalIDType[::Environment].coerce_isolated_input(args[:environment_id]) + end + + # TODO: uncomment following line once lines above are removed + # annotation_source_id = args[:cluster_id] || args[:environment_id] authorized_find!(id: annotation_source_id) end end diff --git a/app/graphql/mutations/notes/base.rb b/app/graphql/mutations/notes/base.rb index 31dabc0a660..d6d5f1e760d 100644 --- a/app/graphql/mutations/notes/base.rb +++ b/app/graphql/mutations/notes/base.rb @@ -11,7 +11,10 @@ module Mutations private def find_object(id:) - GitlabSchema.object_from_id(id) + # TODO: remove explicit coercion once compatibility layer has been removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + id = ::Types::GlobalIDType[::Note].coerce_isolated_input(id) + GitlabSchema.find_by_gid(id) end def check_object_is_noteable!(object) diff --git a/app/graphql/mutations/notes/create/base.rb b/app/graphql/mutations/notes/create/base.rb index f081eac368e..530c15f0c10 100644 --- a/app/graphql/mutations/notes/create/base.rb +++ b/app/graphql/mutations/notes/create/base.rb @@ -9,7 +9,7 @@ module Mutations authorize :create_note argument :noteable_id, - GraphQL::ID_TYPE, + ::Types::GlobalIDType[::Noteable], required: true, description: 'The global id of the resource to add a note to' @@ -42,6 +42,13 @@ module Mutations private + def find_object(id:) + # TODO: remove explicit coercion once compatibility layer has been removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + id = ::Types::GlobalIDType[::Noteable].coerce_isolated_input(id) + GitlabSchema.find_by_gid(id) + end + def create_note_params(noteable, args) { noteable: noteable, diff --git a/app/graphql/mutations/notes/destroy.rb b/app/graphql/mutations/notes/destroy.rb index a81322bc9b7..83a91f07896 100644 --- a/app/graphql/mutations/notes/destroy.rb +++ b/app/graphql/mutations/notes/destroy.rb @@ -8,9 +8,9 @@ module Mutations authorize :admin_note argument :id, - GraphQL::ID_TYPE, - required: true, - description: 'The global id of the note to destroy' + ::Types::GlobalIDType[::Note], + required: true, + description: 'The global id of the note to destroy' def resolve(id:) note = authorized_find!(id: id) diff --git a/app/graphql/mutations/todos/base.rb b/app/graphql/mutations/todos/base.rb index 2a72019fbac..6db863796bc 100644 --- a/app/graphql/mutations/todos/base.rb +++ b/app/graphql/mutations/todos/base.rb @@ -6,7 +6,10 @@ module Mutations private def find_object(id:) - GitlabSchema.object_from_id(id) + # TODO: remove this line when the compatibility layer is removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + id = ::Types::GlobalIDType[::Todo].coerce_isolated_input(id) + GitlabSchema.find_by_gid(id) end def map_to_global_ids(ids) @@ -16,7 +19,7 @@ module Mutations end def to_global_id(id) - ::URI::GID.build(app: GlobalID.app, model_name: Todo.name, model_id: id, params: nil).to_s + Gitlab::GlobalId.as_global_id(id, model_name: Todo.name).to_s end end end diff --git a/app/graphql/mutations/todos/restore_many.rb b/app/graphql/mutations/todos/restore_many.rb index c5e2750768c..ea5f5414134 100644 --- a/app/graphql/mutations/todos/restore_many.rb +++ b/app/graphql/mutations/todos/restore_many.rb @@ -8,7 +8,7 @@ module Mutations MAX_UPDATE_AMOUNT = 50 argument :ids, - [GraphQL::ID_TYPE], + [::Types::GlobalIDType[::Todo]], required: true, description: 'The global ids of the todos to restore (a maximum of 50 is supported at once)' @@ -37,24 +37,18 @@ module Mutations private def gids_of(ids) - ids.map { |id| ::URI::GID.build(app: GlobalID.app, model_name: Todo.name, model_id: id, params: nil).to_s } + ids.map { |id| Gitlab::GlobalId.as_global_id(id, model_name: Todo.name).to_s } end def model_ids_of(ids) ids.map do |gid| - parsed_gid = ::URI::GID.parse(gid) - parsed_gid.model_id.to_i if accessible_todo?(parsed_gid) + # TODO: remove this line when the compatibility layer is removed + # See: https://gitlab.com/gitlab-org/gitlab/-/issues/257883 + gid = ::Types::GlobalIDType[::Todo].coerce_isolated_input(gid) + gid.model_id.to_i end.compact end - def accessible_todo?(gid) - gid.app == GlobalID.app && todo?(gid) - end - - def todo?(gid) - GlobalID.parse(gid)&.model_class&.ancestors&.include?(Todo) - end - def raise_too_many_todos_requested_error raise Gitlab::Graphql::Errors::ArgumentError, 'Too many todos requested.' end diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index ad0f4d03f9a..d55850b5673 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -55,6 +55,9 @@ - if merge_request.assignees.any? %li.d-flex = render 'shared/issuable/assignees', project: merge_request.project, issuable: merge_request + - if Feature.enabled?(:merge_request_reviewers, @project) && merge_request.reviewers.any? + %li.gl-display-flex.issuable-reviewers + = render 'shared/issuable/reviewers', project: merge_request.project, issuable: merge_request = render 'projects/merge_requests/approvals_count', merge_request: merge_request = render 'shared/issuable_meta_data', issuable: merge_request diff --git a/app/views/search/_filter.html.haml b/app/views/search/_filter.html.haml index bee4aff605f..e7febd4638b 100644 --- a/app/views/search/_filter.html.haml +++ b/app/views/search/_filter.html.haml @@ -2,15 +2,14 @@ = hidden_field_tag :group_id, params[:group_id] - if params[:project_id].present? = hidden_field_tag :project_id, params[:project_id] -.dropdown.form-group.mb-lg-0.mx-lg-1 +.dropdown.form-group.mb-lg-0.mx-lg-1{ data: { testid: "group-filter" } } %label.d-block{ for: "dashboard_search_group" } = _("Group") - %button.dropdown-menu-toggle.js-search-group-dropdown.mt-0{ type: "button", id: "dashboard_search_group", data: { toggle: "dropdown", group_id: params[:group_id] } } - %span.dropdown-toggle-text - - if @group.present? - = @group.name - - else - = _("Any") + %button.dropdown-menu-toggle.gl-display-inline-flex.js-search-group-dropdown.gl-mt-0{ type: "button", id: "dashboard_search_group", data: { toggle: "dropdown", group_id: params[:group_id] } } + %span.dropdown-toggle-text.gl-flex-grow-1.str-truncated-100 + = @group&.name || _("Any") + - if @group.present? + = link_to sprite_icon("clear"), url_for(safe_params.except(:project_id, :group_id)), class: 'search-clear js-search-clear has-tooltip', title: _('Clear') = icon("chevron-down") .dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-right = dropdown_title(_("Filter results by group")) @@ -21,12 +20,11 @@ .dropdown.project-filter.form-group.mb-lg-0.mx-lg-1 %label.d-block{ for: "dashboard_search_project" } = _("Project") - %button.dropdown-menu-toggle.js-search-project-dropdown.mt-0{ type: "button", id: "dashboard_search_project", data: { toggle: "dropdown"} } - %span.dropdown-toggle-text - - if @project.present? - = @project.full_name - - else - = _("Any") + %button.dropdown-menu-toggle.gl-display-inline-flex.js-search-project-dropdown.gl-mt-0{ type: "button", id: "dashboard_search_project", data: { toggle: "dropdown", target: '.project-filter' } } + %span.dropdown-toggle-text.gl-flex-grow-1.str-truncated-100 + = @project&.full_name || _("Any") + - if @project.present? + = link_to sprite_icon("clear"), url_for(safe_params.except(:project_id)), class: 'search-clear js-search-clear has-tooltip', title: _('Clear') = icon("chevron-down") .dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-right = dropdown_title(_("Filter results by project")) diff --git a/app/views/search/_form.html.haml b/app/views/search/_form.html.haml index 30ca9d3f5ab..c8fa016662f 100644 --- a/app/views/search/_form.html.haml +++ b/app/views/search/_form.html.haml @@ -10,7 +10,7 @@ .position-relative = search_field_tag :search, params[:search], placeholder: _("Search for projects, issues, etc."), class: "form-control search-text-input js-search-input", id: "dashboard_search", autofocus: true, spellcheck: false = sprite_icon('search', css_class: 'search-icon') - %button.search-clear.js-search-clear{ class: ("hidden" if !params[:search].present?), type: "button", tabindex: "-1" } + %button.search-clear.js-search-clear{ class: [("hidden" if params[:search].blank?), "has-tooltip"], type: "button", tabindex: "-1", title: _('Clear') } = sprite_icon('clear') %span.sr-only = _("Clear search") diff --git a/app/views/shared/issuable/_reviewers.html.haml b/app/views/shared/issuable/_reviewers.html.haml new file mode 100644 index 00000000000..8e66135a20b --- /dev/null +++ b/app/views/shared/issuable/_reviewers.html.haml @@ -0,0 +1,11 @@ +- max_render = 4 +- reviewers_rendering_overflow = issuable.reviewers.size > max_render +- render_count = reviewers_rendering_overflow ? max_render - 1 : max_render +- more_reviewers_count = issuable.reviewers.size - render_count + +- issuable.reviewers.take(render_count).each do |reviewer| # rubocop: disable CodeReuse/ActiveRecord + = link_to_member(@project, reviewer, name: false, title: _("Review requested from %{name}") % { name: reviewer.name}) + +- if more_reviewers_count > 0 + %span{ class: 'avatar-counter has-tooltip', data: { container: 'body', placement: 'bottom', 'line-type' => 'old' }, title: _("+%{more_reviewers_count} more reviewers") % { more_reviewers_count: more_reviewers_count} } + = _("+%{more_reviewers_count}") % { more_reviewers_count: more_reviewers_count} diff --git a/changelogs/unreleased/247895-search-results-filters-should-have-a-clear-behavior.yml b/changelogs/unreleased/247895-search-results-filters-should-have-a-clear-behavior.yml new file mode 100644 index 00000000000..b113ed96e18 --- /dev/null +++ b/changelogs/unreleased/247895-search-results-filters-should-have-a-clear-behavior.yml @@ -0,0 +1,5 @@ +--- +title: Add buttons in the Search page to clear Group and Project filters +merge_request: 42897 +author: +type: added diff --git a/changelogs/unreleased/bw_update_global_id_checks.yml b/changelogs/unreleased/bw_update_global_id_checks.yml new file mode 100644 index 00000000000..2c3d9e81755 --- /dev/null +++ b/changelogs/unreleased/bw_update_global_id_checks.yml @@ -0,0 +1,5 @@ +--- +title: Updated GraphQL mutation input ids to be more type specific +merge_request: 44073 +author: +type: changed diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 9bd305f769a..31e67f617ff 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -32,7 +32,7 @@ input AddAwardEmojiInput { """ The global id of the awardable resource """ - awardableId: ID! + awardableId: AwardableID! """ A unique identifier for the client performing the mutation. @@ -77,7 +77,7 @@ input AddProjectToSecurityDashboardInput { """ ID of the project to be added to Instance Security Dashboard """ - id: ID! + id: ProjectID! } """ @@ -792,7 +792,7 @@ input AwardEmojiAddInput { """ The global id of the awardable resource """ - awardableId: ID! + awardableId: AwardableID! """ A unique identifier for the client performing the mutation. @@ -832,7 +832,7 @@ input AwardEmojiRemoveInput { """ The global id of the awardable resource """ - awardableId: ID! + awardableId: AwardableID! """ A unique identifier for the client performing the mutation. @@ -872,7 +872,7 @@ input AwardEmojiToggleInput { """ The global id of the awardable resource """ - awardableId: ID! + awardableId: AwardableID! """ A unique identifier for the client performing the mutation. @@ -910,6 +910,11 @@ type AwardEmojiTogglePayload { toggledOn: Boolean! } +""" +Identifier of Awardable +""" +scalar AwardableID + type BaseService implements Service { """ Indicates if the service is active @@ -2459,6 +2464,11 @@ Identifier of Clusters::AgentToken """ scalar ClustersAgentTokenID +""" +Identifier of Clusters::Cluster +""" +scalar ClustersClusterID + type Commit { """ Author of the commit @@ -3003,7 +3013,7 @@ input CreateAnnotationInput { """ The global id of the cluster to add an annotation to """ - clusterId: ID + clusterId: ClustersClusterID """ The path to a file defining the dashboard on which the annotation should be added @@ -3023,7 +3033,7 @@ input CreateAnnotationInput { """ The global id of the environment to add an annotation to """ - environmentId: ID + environmentId: EnvironmentID """ Timestamp indicating starting moment to which the annotation relates @@ -3158,7 +3168,7 @@ input CreateDiffNoteInput { """ The global id of the resource to add a note to """ - noteableId: ID! + noteableId: NoteableID! """ The position of this note on a diff @@ -3288,7 +3298,7 @@ input CreateImageDiffNoteInput { """ The global id of the resource to add a note to """ - noteableId: ID! + noteableId: NoteableID! """ The position of this note on a diff @@ -3403,7 +3413,7 @@ input CreateNoteInput { """ The global id of the resource to add a note to """ - noteableId: ID! + noteableId: NoteableID! } """ @@ -5150,7 +5160,7 @@ input DestroyNoteInput { """ The global id of the note to destroy """ - id: ID! + id: NoteID! } """ @@ -5624,7 +5634,7 @@ input DismissVulnerabilityInput { """ ID of the vulnerability to be dismissed """ - id: ID! + id: VulnerabilityID! } """ @@ -5759,6 +5769,11 @@ type EnvironmentEdge { } """ +Identifier of Environment +""" +scalar EnvironmentID + +""" Represents an epic """ type Epic implements CurrentUserTodos & Noteable { @@ -6886,17 +6901,17 @@ input EpicTreeNodeFieldsInputType { """ The id of the epic_issue or issue that the actual epic or issue is switched with """ - adjacentReferenceId: ID + adjacentReferenceId: EpicTreeSortingID """ The id of the epic_issue or epic that is being moved """ - id: ID! + id: EpicTreeSortingID! """ ID of the new parent epic """ - newParentId: ID + newParentId: EpicID """ The type of the switch, after or before allowed @@ -6911,7 +6926,7 @@ input EpicTreeReorderInput { """ The id of the base epic of the tree """ - baseEpicId: ID! + baseEpicId: EpicID! """ A unique identifier for the client performing the mutation. @@ -6940,6 +6955,11 @@ type EpicTreeReorderPayload { } """ +Identifier of EpicTreeSorting +""" +scalar EpicTreeSortingID + +""" Epic ID wildcard values """ enum EpicWildcardId { @@ -12020,6 +12040,11 @@ type NamespaceEdge { } """ +Identifier of Namespace +""" +scalar NamespaceID + +""" Autogenerated input type of NamespaceIncreaseStorageTemporarily """ input NamespaceIncreaseStorageTemporarilyInput { @@ -12031,7 +12056,7 @@ input NamespaceIncreaseStorageTemporarilyInput { """ The global id of the namespace to mutate """ - id: ID! + id: NamespaceID! } """ @@ -12228,6 +12253,11 @@ type NoteEdge { node: Note } +""" +Identifier of Note +""" +scalar NoteID + type NotePermissions { """ Indicates the user can perform `admin_note` on this resource @@ -12308,6 +12338,11 @@ interface Noteable { } """ +Identifier of Noteable +""" +scalar NoteableID + +""" Represents a package """ type Package { @@ -14502,6 +14537,11 @@ type ProjectEdge { } """ +Identifier of Project +""" +scalar ProjectID + +""" Represents a Project Membership """ type ProjectMember implements MemberInterface { @@ -15779,7 +15819,7 @@ input RemoveAwardEmojiInput { """ The global id of the awardable resource """ - awardableId: ID! + awardableId: AwardableID! """ A unique identifier for the client performing the mutation. @@ -18267,6 +18307,11 @@ type TodoEdge { } """ +Identifier of Todo +""" +scalar TodoID + +""" Autogenerated input type of TodoMarkDone """ input TodoMarkDoneInput { @@ -18328,7 +18373,7 @@ input TodoRestoreManyInput { """ The global ids of the todos to restore (a maximum of 50 is supported at once) """ - ids: [ID!]! + ids: [TodoID!]! } """ @@ -18455,7 +18500,7 @@ input ToggleAwardEmojiInput { """ The global id of the awardable resource """ - awardableId: ID! + awardableId: AwardableID! """ A unique identifier for the client performing the mutation. @@ -18772,7 +18817,7 @@ input UpdateBoardInput { """ The id of user to be assigned to the board. """ - assigneeId: ID + assigneeId: UserID """ A unique identifier for the client performing the mutation. @@ -18792,7 +18837,7 @@ input UpdateBoardInput { """ The board global id. """ - id: ID! + id: BoardID! """ The IDs of labels to be added to the board. @@ -18807,7 +18852,7 @@ input UpdateBoardInput { """ The id of milestone to be assigned to the board. """ - milestoneId: ID + milestoneId: MilestoneID """ Name of the board @@ -20296,7 +20341,7 @@ input VulnerabilityDismissInput { """ ID of the vulnerability to be dismissed """ - id: ID! + id: VulnerabilityID! } """ diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index ddf0006f9e2..e3446f22e01 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -111,7 +111,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "AwardableID", "ofType": null } }, @@ -227,7 +227,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "ProjectID", "ofType": null } }, @@ -1993,7 +1993,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "AwardableID", "ofType": null } }, @@ -2109,7 +2109,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "AwardableID", "ofType": null } }, @@ -2225,7 +2225,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "AwardableID", "ofType": null } }, @@ -2346,6 +2346,16 @@ "possibleTypes": null }, { + "kind": "SCALAR", + "name": "AwardableID", + "description": "Identifier of Awardable", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "OBJECT", "name": "BaseService", "description": null, @@ -6674,6 +6684,16 @@ "possibleTypes": null }, { + "kind": "SCALAR", + "name": "ClustersClusterID", + "description": "Identifier of Clusters::Cluster", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "OBJECT", "name": "Commit", "description": null, @@ -8039,7 +8059,7 @@ "description": "The global id of the environment to add an annotation to", "type": { "kind": "SCALAR", - "name": "ID", + "name": "EnvironmentID", "ofType": null }, "defaultValue": null @@ -8049,7 +8069,7 @@ "description": "The global id of the cluster to add an annotation to", "type": { "kind": "SCALAR", - "name": "ID", + "name": "ClustersClusterID", "ofType": null }, "defaultValue": null @@ -8448,7 +8468,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "NoteableID", "ofType": null } }, @@ -8796,7 +8816,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "NoteableID", "ofType": null } }, @@ -9084,7 +9104,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "NoteableID", "ofType": null } }, @@ -14068,7 +14088,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "NoteID", "ofType": null } }, @@ -15517,7 +15537,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "VulnerabilityID", "ofType": null } }, @@ -16000,6 +16020,16 @@ "possibleTypes": null }, { + "kind": "SCALAR", + "name": "EnvironmentID", + "description": "Identifier of Environment", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "OBJECT", "name": "Epic", "description": "Represents an epic", @@ -19077,7 +19107,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "EpicTreeSortingID", "ofType": null } }, @@ -19088,7 +19118,7 @@ "description": "The id of the epic_issue or issue that the actual epic or issue is switched with", "type": { "kind": "SCALAR", - "name": "ID", + "name": "EpicTreeSortingID", "ofType": null }, "defaultValue": null @@ -19108,7 +19138,7 @@ "description": "ID of the new parent epic", "type": { "kind": "SCALAR", - "name": "ID", + "name": "EpicID", "ofType": null }, "defaultValue": null @@ -19132,7 +19162,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "EpicID", "ofType": null } }, @@ -19221,6 +19251,16 @@ "possibleTypes": null }, { + "kind": "SCALAR", + "name": "EpicTreeSortingID", + "description": "Identifier of EpicTreeSorting", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "ENUM", "name": "EpicWildcardId", "description": "Epic ID wildcard values", @@ -35420,6 +35460,16 @@ "possibleTypes": null }, { + "kind": "SCALAR", + "name": "NamespaceID", + "description": "Identifier of Namespace", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "INPUT_OBJECT", "name": "NamespaceIncreaseStorageTemporarilyInput", "description": "Autogenerated input type of NamespaceIncreaseStorageTemporarily", @@ -35433,7 +35483,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "NamespaceID", "ofType": null } }, @@ -36041,6 +36091,16 @@ "possibleTypes": null }, { + "kind": "SCALAR", + "name": "NoteID", + "description": "Identifier of Note", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "OBJECT", "name": "NotePermissions", "description": null, @@ -36315,6 +36375,16 @@ ] }, { + "kind": "SCALAR", + "name": "NoteableID", + "description": "Identifier of Noteable", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "OBJECT", "name": "Package", "description": "Represents a package", @@ -41986,6 +42056,16 @@ "possibleTypes": null }, { + "kind": "SCALAR", + "name": "ProjectID", + "description": "Identifier of Project", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "OBJECT", "name": "ProjectMember", "description": "Represents a Project Membership", @@ -45596,7 +45676,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "AwardableID", "ofType": null } }, @@ -53289,6 +53369,16 @@ "possibleTypes": null }, { + "kind": "SCALAR", + "name": "TodoID", + "description": "Identifier of Todo", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { "kind": "INPUT_OBJECT", "name": "TodoMarkDoneInput", "description": "Autogenerated input type of TodoMarkDone", @@ -53449,7 +53539,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "TodoID", "ofType": null } } @@ -53858,7 +53948,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "AwardableID", "ofType": null } }, @@ -54776,7 +54866,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "BoardID", "ofType": null } }, @@ -54817,7 +54907,7 @@ "description": "The id of user to be assigned to the board.", "type": { "kind": "SCALAR", - "name": "ID", + "name": "UserID", "ofType": null }, "defaultValue": null @@ -54827,7 +54917,7 @@ "description": "The id of milestone to be assigned to the board.", "type": { "kind": "SCALAR", - "name": "ID", + "name": "MilestoneID", "ofType": null }, "defaultValue": null @@ -58883,7 +58973,7 @@ "name": null, "ofType": { "kind": "SCALAR", - "name": "ID", + "name": "VulnerabilityID", "ofType": null } }, diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md index 34cf25142e4..ab15faa4e1b 100644 --- a/doc/ci/yaml/README.md +++ b/doc/ci/yaml/README.md @@ -1698,20 +1698,17 @@ while just `/issue/` would also match a branch called `severe-issues`. #### Supported `only`/`except` regexp syntax -CAUTION: **Warning:** -This is a breaking change that was introduced with GitLab 11.9.4. - -In GitLab 11.9.4, GitLab begun internally converting regexp used +In GitLab 11.9.4, GitLab began internally converting the regexp used in `only` and `except` parameters to [RE2](https://github.com/google/re2/wiki/Syntax). -This means that only subset of features provided by [Ruby Regexp](https://ruby-doc.org/core/Regexp.html) -is supported. [RE2](https://github.com/google/re2/wiki/Syntax) limits the set of features -provided due to computational complexity, which means some features became unavailable in GitLab 11.9.4. -For example, negative lookaheads. +[RE2](https://github.com/google/re2/wiki/Syntax) limits the set of available features +due to computational complexity, and some features, like negative lookaheads, became unavailable. +Only a subset of features provided by [Ruby Regexp](https://ruby-doc.org/core/Regexp.html) +are now supported. -For GitLab versions from 11.9.7 and up to GitLab 12.0, GitLab provides a feature flag that can be -enabled by administrators that allows users to use unsafe regexp syntax. This brings compatibility -with previously allowed syntax version and allows users to gracefully migrate to the new syntax. +From GitLab 11.9.7 to GitLab 12.0, GitLab provided a feature flag to +let you use the unsafe regexp syntax. This flag allowed +compatibility with the previous syntax version so you could gracefully migrate to the new syntax. ```ruby Feature.enable(:allow_unsafe_ruby_regexp) @@ -1767,7 +1764,7 @@ added if the following is true: - `(any listed refs are true) OR (any listed variables are true) OR (any listed changes are true) OR (a chosen Kubernetes status matches)` -In the example below, the `test` job will **not** be created when **any** of the following are true: +In the example below, the `test` job is **not** created when **any** of the following are true: - The pipeline runs for the `master` branch. - There are changes to the `README.md` file in the root directory of the repository. @@ -1819,12 +1816,11 @@ deploy: > `variables` policy introduced in GitLab 10.7. -The `variables` keyword defines variables expressions. In other words, -you can use predefined variables / project / group or -environment-scoped variables to define an expression that GitLab -evaluates to decide whether a job should be created or not. +The `variables` keyword defines variable expressions. + +These expressions determine whether or not a job should be created. -Examples of using variables expressions: +Examples of using variable expressions: ```yaml deploy: @@ -1894,22 +1890,21 @@ docker build: - more_scripts/*.{rb,py,sh} ``` -In the scenario above, when pushing commits to an existing branch in GitLab, -it creates and triggers the `docker build` job, provided that one of the -commits contains changes to any of the following: +When you push commits to an existing branch, +the `docker build` job is created, but only if changes were made to any of the following: - The `Dockerfile` file. -- Any of the files inside `docker/scripts/` directory. -- Any of the files and subdirectories inside the `dockerfiles` directory. -- Any of the files with `rb`, `py`, `sh` extensions inside the `more_scripts` directory. +- Any of the files in the `docker/scripts/` directory. +- Any of the files and subdirectories in the `dockerfiles` directory. +- Any of the files with `rb`, `py`, `sh` extensions in the `more_scripts` directory. CAUTION: **Warning:** -If using `only:changes` with [only allow merge requests to be merged if the pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds), -undesired behavior could result if you don't [also use `only:merge_requests`](#using-onlychanges-with-pipelines-for-merge-requests). +If you use `only:changes` with [only allow merge requests to be merged if the pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds), +undesired behavior can result if you don't [also use `only:merge_requests`](#using-onlychanges-with-pipelines-for-merge-requests). You can also use glob patterns to match multiple files in either the root directory -of the repository, or in _any_ directory within the repository, but they must be wrapped -in double quotes or GitLab can't parse the `.gitlab-ci.yml`. For example: +of the repository, or in _any_ directory within the repository. However, they must be wrapped +in double quotes or GitLab can't parse them. For example: ```yaml test: @@ -1922,10 +1917,8 @@ test: - "**/*.sql" ``` -The following example skips the `build` job if a change is detected in any file -with a `.md` extension in the root directory of the repository. This means that if you change multiple files, -but only one file is a `.md` file, the `build` job is still skipped and does -not run for the other files. +You can skip a job if a change is detected in any file with a +`.md` extension in the root directory of the repository: ```yaml build: @@ -1935,13 +1928,13 @@ build: - "*.md" ``` -CAUTION: **Warning:** -There are some points to be aware of when -[using this feature with new branches or tags *without* pipelines for merge requests](#using-onlychanges-without-pipelines-for-merge-requests). +If you change multiple files, but only one file ends in `.md`, +the `build` job is still skipped. The job does not run for any of the files. -CAUTION: **Warning:** -There are some points to be aware of when -[using this feature with scheduled pipelines](#using-onlychanges-with-scheduled-pipelines). +Read more about how to use this feature with: + +- [New branches or tags *without* pipelines for merge requests](#using-onlychanges-without-pipelines-for-merge-requests). +- [Scheduled pipelines](#using-onlychanges-with-scheduled-pipelines). ##### Using `only:changes` with pipelines for merge requests diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6d501c0db91..d54af0a471c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -997,6 +997,12 @@ msgstr[1] "" msgid "+%{approvers} more approvers" msgstr "" +msgid "+%{more_reviewers_count}" +msgstr "" + +msgid "+%{more_reviewers_count} more reviewers" +msgstr "" + msgid "+%{tags} more" msgstr "" @@ -22196,6 +22202,9 @@ msgstr "" msgid "Review App|View latest app" msgstr "" +msgid "Review requested from %{name}" +msgstr "" + msgid "Review the process for configuring service providers in your identity provider — in this case, GitLab is the \"service provider\" or \"relying party\"." msgstr "" diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb index c254be4800b..709467eaf3c 100644 --- a/qa/qa/runtime/env.rb +++ b/qa/qa/runtime/env.rb @@ -81,6 +81,10 @@ module QA ENV['GITLAB_QA_ADMIN_ACCESS_TOKEN'] end + def ci_job_id + ENV['CI_JOB_ID'] + end + def ci_project_name ENV['CI_PROJECT_NAME'] end diff --git a/qa/qa/support/json_formatter.rb b/qa/qa/support/json_formatter.rb index f6e40436ec8..8abdee35c14 100644 --- a/qa/qa/support/json_formatter.rb +++ b/qa/qa/support/json_formatter.rb @@ -50,7 +50,8 @@ module QA pending_message: example.execution_result.pending_message, testcase: example.metadata[:testcase], quarantine: example.metadata[:quarantine], - screenshot: example.metadata[:screenshot] + screenshot: example.metadata[:screenshot], + ci_job_id: QA::Runtime::Env.ci_job_id } end diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb index 4531ef40901..36d28ae2822 100644 --- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb +++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb @@ -8,6 +8,10 @@ RSpec.describe 'Merge requests > User lists merge requests' do let(:project) { create(:project, :public, :repository) } let(:user) { create(:user) } + let(:user2) { create(:user) } + let(:user3) { create(:user) } + let(:user4) { create(:user) } + let(:user5) { create(:user) } before do @fix = create(:merge_request, @@ -15,6 +19,7 @@ RSpec.describe 'Merge requests > User lists merge requests' do source_project: project, source_branch: 'fix', assignees: [user], + reviewers: [user, user2, user3, user4, user5], milestone: create(:milestone, project: project, due_date: '2013-12-11'), created_at: 1.minute.ago, updated_at: 1.minute.ago) @@ -23,6 +28,7 @@ RSpec.describe 'Merge requests > User lists merge requests' do source_project: project, source_branch: 'markdown', assignees: [user], + reviewers: [user, user2, user3, user4], milestone: create(:milestone, project: project, due_date: '2013-12-12'), created_at: 2.minutes.ago, updated_at: 2.minutes.ago) @@ -34,6 +40,37 @@ RSpec.describe 'Merge requests > User lists merge requests' do updated_at: 10.seconds.ago) end + context 'when merge_request_reviewers is turned on' do + before do + stub_feature_flags(merge_request_reviewers: true) + visit_merge_requests(project, reviewer_id: user.id) + end + + it 'has reviewers in MR list' do + expect(page).to have_css('.issuable-reviewers') + end + + it 'shows reviewers avatar count badge if more_reviewers_count > 4' do + first_issuable_reviewers = first('.issuable-reviewers') + + expect(first_issuable_reviewers).to have_content('2') + expect(first_issuable_reviewers).to have_css('.avatar-counter') + end + + it 'does not show reviewers avatar count badge if more_reviewers_count <= 4' do + expect(page.all('.issuable-reviewers')[1]).not_to have_css('.avatar-counter') + end + end + + context 'when merge_request_reviewers is turned false' do + it 'has no reviewers in MR list' do + stub_feature_flags(merge_request_reviewers: false) + visit_merge_requests(project, reviewer_id: user.id) + + expect(page).not_to have_css('.issuable-reviewers') + end + end + it 'filters on no assignee' do visit_merge_requests(project, assignee_id: IssuableFinder::Params::FILTER_NONE) diff --git a/spec/features/search/user_uses_search_filters_spec.rb b/spec/features/search/user_uses_search_filters_spec.rb index f39a1f8fe37..080cced21c3 100644 --- a/spec/features/search/user_uses_search_filters_spec.rb +++ b/spec/features/search/user_uses_search_filters_spec.rb @@ -12,12 +12,12 @@ RSpec.describe 'User uses search filters', :js do project.add_reporter(user) group.add_owner(user) sign_in(user) - - visit(search_path) end context 'when filtering by group' do it 'shows group projects' do + visit search_path + find('.js-search-group-dropdown').click wait_for_requests @@ -36,10 +36,27 @@ RSpec.describe 'User uses search filters', :js do expect(page).to have_link(group_project.full_name) end end + + context 'when the group filter is set' do + before do + visit search_path(search: "test", group_id: group.id, project_id: project.id) + end + + describe 'clear filter button' do + it 'removes Group and Project filters' do + link = find('[data-testid="group-filter"] .js-search-clear') + params = CGI.parse(URI.parse(link[:href]).query) + + expect(params).not_to include(:group_id, :project_id) + end + end + end end context 'when filtering by project' do it 'shows a project' do + visit search_path + page.within('.project-filter') do find('.js-search-project-dropdown').click @@ -50,5 +67,22 @@ RSpec.describe 'User uses search filters', :js do expect(find('.js-search-project-dropdown')).to have_content(project.full_name) end + + context 'when the project filter is set' do + before do + visit search_path(search: "test", project_id: project.id) + end + + let(:query) { { project_id: project.id } } + + describe 'clear filter button' do + it 'removes Project filters' do + link = find('.project-filter .js-search-clear') + params = CGI.parse(URI.parse(link[:href]).query) + + expect(params).not_to include(:project_id) + end + end + end end end diff --git a/spec/graphql/mutations/todos/mark_done_spec.rb b/spec/graphql/mutations/todos/mark_done_spec.rb index 51ad3e1a5d7..b5f2ff5d044 100644 --- a/spec/graphql/mutations/todos/mark_done_spec.rb +++ b/spec/graphql/mutations/todos/mark_done_spec.rb @@ -52,7 +52,8 @@ RSpec.describe Mutations::Todos::MarkDone do end it 'ignores invalid GIDs' do - expect { mutation.resolve(id: 'invalid_gid') }.to raise_error(Gitlab::Graphql::Errors::ArgumentError) + expect { mutation.resolve(id: author.to_global_id.to_s) } + .to raise_error(::GraphQL::CoercionError) expect(todo1.reload.state).to eq('pending') expect(todo2.reload.state).to eq('done') diff --git a/spec/graphql/mutations/todos/restore_many_spec.rb b/spec/graphql/mutations/todos/restore_many_spec.rb index b3b3e057745..59995e33f2d 100644 --- a/spec/graphql/mutations/todos/restore_many_spec.rb +++ b/spec/graphql/mutations/todos/restore_many_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Mutations::Todos::RestoreMany do + include GraphqlHelpers + let_it_be(:current_user) { create(:user) } let_it_be(:author) { create(:user) } let_it_be(:other_user) { create(:user) } @@ -44,8 +46,9 @@ RSpec.describe Mutations::Todos::RestoreMany do expect_states_were_not_changed end - it 'ignores invalid GIDs' do - expect { mutation.resolve(ids: ['invalid_gid']) }.to raise_error(URI::BadURIError) + it 'raises an error with invalid or non-Todo GIDs' do + expect { mutation.resolve(ids: [author.to_global_id.to_s]) } + .to raise_error(GraphQL::CoercionError) expect_states_were_not_changed end @@ -78,38 +81,12 @@ RSpec.describe Mutations::Todos::RestoreMany do it 'fails if too many todos are requested for update' do expect { restore_mutation([todo1] * 51) }.to raise_error(Gitlab::Graphql::Errors::ArgumentError) end - - it 'does not update todos from another app' do - todo4 = create(:todo) - todo4_gid = ::URI::GID.parse("gid://otherapp/Todo/#{todo4.id}") - - result = mutation.resolve(ids: [todo4_gid.to_s]) - - expect(result[:updated_ids]).to be_empty - - expect_states_were_not_changed - end - - it 'does not update todos from another model' do - todo4 = create(:todo) - todo4_gid = ::URI::GID.parse("gid://#{GlobalID.app}/Project/#{todo4.id}") - - result = mutation.resolve(ids: [todo4_gid.to_s]) - - expect(result[:updated_ids]).to be_empty - - expect_states_were_not_changed - end end def restore_mutation(todos) mutation.resolve(ids: todos.map { |todo| global_id_of(todo) } ) end - def global_id_of(todo) - todo.to_global_id.to_s - end - def expect_states_were_not_changed expect(todo1.reload.state).to eq('done') expect(todo2.reload.state).to eq('pending') diff --git a/spec/graphql/mutations/todos/restore_spec.rb b/spec/graphql/mutations/todos/restore_spec.rb index 9043d7a44a8..22fb1bba7a8 100644 --- a/spec/graphql/mutations/todos/restore_spec.rb +++ b/spec/graphql/mutations/todos/restore_spec.rb @@ -3,6 +3,8 @@ require 'spec_helper' RSpec.describe Mutations::Todos::Restore do + include GraphqlHelpers + let_it_be(:current_user) { create(:user) } let_it_be(:author) { create(:user) } let_it_be(:other_user) { create(:user) } @@ -49,8 +51,9 @@ RSpec.describe Mutations::Todos::Restore do expect(other_user_todo.reload.state).to eq('done') end - it 'ignores invalid GIDs' do - expect { mutation.resolve(id: 'invalid_gid') }.to raise_error(Gitlab::Graphql::Errors::ArgumentError) + it 'raises error for invalid GID' do + expect { mutation.resolve(id: author.to_global_id.to_s) } + .to raise_error(::GraphQL::CoercionError) expect(todo1.reload.state).to eq('done') expect(todo2.reload.state).to eq('pending') @@ -61,8 +64,4 @@ RSpec.describe Mutations::Todos::Restore do def restore_mutation(todo) mutation.resolve(id: global_id_of(todo)) end - - def global_id_of(todo) - todo.to_global_id.to_s - end end diff --git a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb index 1d38bb39d59..3aaebb5095a 100644 --- a/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb +++ b/spec/requests/api/graphql/mutations/award_emojis/add_spec.rb @@ -45,8 +45,9 @@ RSpec.describe 'Adding an AwardEmoji' do it_behaves_like 'a mutation that does not create an AwardEmoji' - it_behaves_like 'a mutation that returns top-level errors', - errors: ['Cannot award emoji to this resource'] + it_behaves_like 'a mutation that returns top-level errors' do + let(:match_errors) { include(/was provided invalid value for awardableId/) } + end end context 'when the given awardable is an Awardable but still cannot be awarded an emoji' do diff --git a/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb index c6e8800de1f..7cd39f93ae7 100644 --- a/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb +++ b/spec/requests/api/graphql/mutations/award_emojis/remove_spec.rb @@ -50,8 +50,9 @@ RSpec.describe 'Removing an AwardEmoji' do it_behaves_like 'a mutation that does not destroy an AwardEmoji' - it_behaves_like 'a mutation that returns top-level errors', - errors: ['Cannot award emoji to this resource'] + it_behaves_like 'a mutation that returns top-level errors' do + let(:match_errors) { include(/was provided invalid value for awardableId/) } + end end context 'when the given awardable is an Awardable' do diff --git a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb index 2df59ce97ca..6910ad80a11 100644 --- a/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb +++ b/spec/requests/api/graphql/mutations/award_emojis/toggle_spec.rb @@ -44,8 +44,9 @@ RSpec.describe 'Toggling an AwardEmoji' do it_behaves_like 'a mutation that does not create or destroy an AwardEmoji' - it_behaves_like 'a mutation that returns top-level errors', - errors: ['Cannot award emoji to this resource'] + it_behaves_like 'a mutation that returns top-level errors' do + let(:match_errors) { include(/was provided invalid value for awardableId/) } + end end context 'when the given awardable is an Awardable but still cannot be awarded an emoji' do diff --git a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb index 10ca2cf1cf8..81d13b29dde 100644 --- a/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb +++ b/spec/requests/api/graphql/mutations/metrics/dashboard/annotations/create_spec.rb @@ -101,7 +101,9 @@ RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create do graphql_mutation(:create_annotation, variables) end - it_behaves_like 'a mutation that returns top-level errors', errors: ['invalid_id is not a valid GitLab ID.'] + it_behaves_like 'a mutation that returns top-level errors' do + let(:match_errors) { include(/is not a valid Global ID/) } + end end end end @@ -109,7 +111,7 @@ RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create do context 'when annotation source is cluster' do let(:mutation) do variables = { - cluster_id: GitlabSchema.id_from_object(cluster).to_s, + cluster_id: cluster.to_global_id.to_s, starting_at: starting_at, ending_at: ending_at, dashboard_path: dashboard_path, @@ -188,15 +190,17 @@ RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create do graphql_mutation(:create_annotation, variables) end - it_behaves_like 'a mutation that returns top-level errors', errors: ['invalid_id is not a valid GitLab ID.'] + it_behaves_like 'a mutation that returns top-level errors' do + let(:match_errors) { include(/is not a valid Global ID/) } + end end end context 'when both environment_id and cluster_id are provided' do let(:mutation) do variables = { - environment_id: GitlabSchema.id_from_object(environment).to_s, - cluster_id: GitlabSchema.id_from_object(cluster).to_s, + environment_id: environment.to_global_id.to_s, + cluster_id: cluster.to_global_id.to_s, starting_at: starting_at, ending_at: ending_at, dashboard_path: dashboard_path, @@ -210,14 +214,14 @@ RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create do end context 'when a non-cluster or environment id is provided' do + let(:gid) { { environment_id: project.to_global_id.to_s } } let(:mutation) do variables = { - environment_id: GitlabSchema.id_from_object(project).to_s, starting_at: starting_at, ending_at: ending_at, dashboard_path: dashboard_path, description: description - } + }.merge!(gid) graphql_mutation(:create_annotation, variables) end @@ -226,6 +230,18 @@ RSpec.describe Mutations::Metrics::Dashboard::Annotations::Create do project.add_developer(current_user) end - it_behaves_like 'a mutation that returns top-level errors', errors: [described_class::INVALID_ANNOTATION_SOURCE_ERROR] + describe 'non-environment id' do + it_behaves_like 'a mutation that returns top-level errors' do + let(:match_errors) { include(/does not represent an instance of Environment/) } + end + end + + describe 'non-cluster id' do + let(:gid) { { cluster_id: project.to_global_id.to_s } } + + it_behaves_like 'a mutation that returns top-level errors' do + let(:match_errors) { include(/does not represent an instance of Clusters::Cluster/) } + end + end end end diff --git a/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb b/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb index 8bf8b96aff5..2bc04713e08 100644 --- a/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb +++ b/spec/requests/api/graphql/mutations/todos/mark_done_spec.rb @@ -76,8 +76,8 @@ RSpec.describe 'Marking todos done' do end context 'when using an invalid gid' do - let(:input) { { id: 'invalid_gid' } } - let(:invalid_gid_error) { 'invalid_gid is not a valid GitLab ID.' } + let(:input) { { id: GitlabSchema.id_from_object(author).to_s } } + let(:invalid_gid_error) { "\"#{input[:id]}\" does not represent an instance of #{todo1.class}" } it 'contains the expected error' do post_graphql_mutation(mutation, current_user: current_user) diff --git a/spec/requests/api/graphql/mutations/todos/restore_spec.rb b/spec/requests/api/graphql/mutations/todos/restore_spec.rb index 8451dcdf587..6cae0492ac4 100644 --- a/spec/requests/api/graphql/mutations/todos/restore_spec.rb +++ b/spec/requests/api/graphql/mutations/todos/restore_spec.rb @@ -76,8 +76,8 @@ RSpec.describe 'Restoring Todos' do end context 'when using an invalid gid' do - let(:input) { { id: 'invalid_gid' } } - let(:invalid_gid_error) { 'invalid_gid is not a valid GitLab ID.' } + let(:input) { { id: GitlabSchema.id_from_object(author).to_s } } + let(:invalid_gid_error) { "\"#{input[:id]}\" does not represent an instance of #{todo1.class}" } it 'contains the expected error' do post_graphql_mutation(mutation, current_user: current_user) diff --git a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb index 522211340ea..24c8a247c93 100644 --- a/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb +++ b/spec/support/shared_examples/graphql/notes_creation_shared_examples.rb @@ -52,11 +52,15 @@ RSpec.shared_examples 'a Note mutation when the given resource id is not for a N it_behaves_like 'a Note mutation that does not create a Note' - it_behaves_like 'a mutation that returns top-level errors', errors: ['Cannot add notes to this resource'] + it_behaves_like 'a mutation that returns top-level errors' do + let(:match_errors) { include(/ does not represent an instance of Noteable/) } + end end RSpec.shared_examples 'a Note mutation when the given resource id is not for a Note' do let(:note) { create(:issue) } - it_behaves_like 'a mutation that returns top-level errors', errors: ['Resource is not a note'] + it_behaves_like 'a mutation that returns top-level errors' do + let(:match_errors) { include(/does not represent an instance of Note/) } + end end |