summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-09-30 09:09:43 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-09-30 09:09:43 +0000
commit14846d722eb52ea70bc3092eb5da80ce1c2e9750 (patch)
tree34700bc9c8c7cddbf955bc079a174ca75e099903
parentbfb305ef516d8d6c07e91024a16772070d09afe0 (diff)
downloadgitlab-ce-14846d722eb52ea70bc3092eb5da80ce1c2e9750.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.rubocop_todo/rspec/described_class.yml1
-rw-r--r--.rubocop_todo/rspec/predicate_matcher.yml2
-rw-r--r--.rubocop_todo/rspec/scattered_let.yml2
-rw-r--r--.rubocop_todo/style/format_string.yml1
-rw-r--r--app/graphql/graphql_triggers.rb8
-rw-r--r--app/models/concerns/timebox.rb92
-rw-r--r--app/models/iteration.rb6
-rw-r--r--app/models/milestone.rb91
-rw-r--r--app/models/project.rb1
-rw-r--r--app/models/user_detail.rb3
-rw-r--r--app/services/merge_requests/base_service.rb1
-rw-r--r--config/feature_flags/development/operational_vulnerabilities_filters.yml2
-rw-r--r--config/feature_flags/development/scan_execution_rule_mode.yml2
-rw-r--r--doc/api/groups.md9
-rw-r--r--doc/user/application_security/generate_test_vulnerabilities/index.md16
-rw-r--r--doc/user/project/insights/index.md141
-rw-r--r--jest.config.base.js2
-rw-r--r--locale/gitlab.pot11
-rw-r--r--package.json1
-rw-r--r--spec/features/issues/related_issues_spec.rb116
-rw-r--r--spec/features/projects/ci/lint_spec.rb9
-rw-r--r--spec/frontend/__helpers__/graphql_transformer.js8
-rw-r--r--spec/graphql/graphql_triggers_spec.rb14
-rw-r--r--spec/models/milestone_spec.rb128
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/services/merge_requests/update_reviewers_service_spec.rb16
-rw-r--r--spec/services/merge_requests/update_service_spec.rb14
-rw-r--r--spec/support/rspec_order_todo.yml2
-rw-r--r--spec/support/shared_examples/models/concerns/timebox_shared_examples.rb118
-rw-r--r--yarn.lock5
30 files changed, 438 insertions, 385 deletions
diff --git a/.rubocop_todo/rspec/described_class.yml b/.rubocop_todo/rspec/described_class.yml
index 8304704985b..516eda12675 100644
--- a/.rubocop_todo/rspec/described_class.yml
+++ b/.rubocop_todo/rspec/described_class.yml
@@ -69,7 +69,6 @@ RSpec/DescribedClass:
- 'ee/spec/services/resource_access_tokens/create_service_spec.rb'
- 'ee/spec/services/security/ingestion/tasks/update_vulnerability_uuids_spec.rb'
- 'ee/spec/services/users/captcha_challenge_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
- 'ee/spec/workers/concerns/elastic/indexing_control_spec.rb'
- 'ee/spec/workers/elastic/migration_worker_spec.rb'
- 'ee/spec/workers/geo/secondary/registry_consistency_worker_spec.rb'
diff --git a/.rubocop_todo/rspec/predicate_matcher.yml b/.rubocop_todo/rspec/predicate_matcher.yml
index 53ee031b8ee..e4215a0e6d0 100644
--- a/.rubocop_todo/rspec/predicate_matcher.yml
+++ b/.rubocop_todo/rspec/predicate_matcher.yml
@@ -99,8 +99,6 @@ RSpec/PredicateMatcher:
- 'ee/spec/services/jira/requests/issues/list_service_spec.rb'
- 'ee/spec/services/milestones/promote_service_spec.rb'
- 'ee/spec/services/protected_environments/create_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/findings/create_from_security_finding_service_spec.rb'
- 'ee/spec/services/vulnerabilities/manually_create_service_spec.rb'
- 'ee/spec/services/vulnerability_exports/export_service_spec.rb'
- 'ee/spec/support/shared_examples/graphql/mutations/dast_on_demand_scans_shared_examples.rb'
diff --git a/.rubocop_todo/rspec/scattered_let.yml b/.rubocop_todo/rspec/scattered_let.yml
index faf2000253a..61f1a09589f 100644
--- a/.rubocop_todo/rspec/scattered_let.yml
+++ b/.rubocop_todo/rspec/scattered_let.yml
@@ -98,8 +98,6 @@ RSpec/ScatteredLet:
- 'ee/spec/services/personal_access_tokens/groups/update_lifetime_service_spec.rb'
- 'ee/spec/services/sbom/ingestion/occurrence_map_spec.rb'
- 'ee/spec/services/security/report_summary_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
- - 'ee/spec/services/vulnerabilities/findings/create_from_security_finding_service_spec.rb'
- 'ee/spec/services/vulnerabilities/security_finding/create_issue_service_spec.rb'
- 'ee/spec/views/subscriptions/groups/edit.html.haml_spec.rb'
- 'ee/spec/workers/concerns/update_orchestration_policy_configuration_spec.rb'
diff --git a/.rubocop_todo/style/format_string.yml b/.rubocop_todo/style/format_string.yml
index 66c368a7a52..adcb8b70f5a 100644
--- a/.rubocop_todo/style/format_string.yml
+++ b/.rubocop_todo/style/format_string.yml
@@ -265,7 +265,6 @@ Style/FormatString:
- 'ee/app/services/vulnerabilities/destroy_dismissal_feedback_service.rb'
- 'ee/app/services/vulnerabilities/dismiss_service.rb'
- 'ee/app/services/vulnerabilities/finding_dismiss_service.rb'
- - 'ee/app/services/vulnerabilities/findings/create_from_security_finding_service.rb'
- 'ee/app/services/vulnerabilities/historical_statistics/adjustment_service.rb'
- 'ee/app/services/vulnerabilities/statistics/adjustment_service.rb'
- 'ee/app/services/vulnerability_external_issue_links/create_service.rb'
diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb
index 2d25a675a46..02b05a24578 100644
--- a/app/graphql/graphql_triggers.rb
+++ b/app/graphql/graphql_triggers.rb
@@ -24,6 +24,14 @@ module GraphqlTriggers
def self.issuable_dates_updated(issuable)
GitlabSchema.subscriptions.trigger('issuableDatesUpdated', { issuable_id: issuable.to_gid }, issuable)
end
+
+ def self.merge_request_reviewers_updated(merge_request)
+ GitlabSchema.subscriptions.trigger(
+ 'mergeRequestReviewersUpdated',
+ { issuable_id: merge_request.to_gid },
+ merge_request
+ )
+ end
end
GraphqlTriggers.prepend_mod
diff --git a/app/models/concerns/timebox.rb b/app/models/concerns/timebox.rb
index d53594eb5af..5b74e88429c 100644
--- a/app/models/concerns/timebox.rb
+++ b/app/models/concerns/timebox.rb
@@ -3,13 +3,10 @@
module Timebox
extend ActiveSupport::Concern
- include AtomicInternalId
include CacheMarkdownField
include Gitlab::SQL::Pattern
- include IidRoutes
include Referable
include StripAttribute
- include FromUnion
TimeboxStruct = Struct.new(:title, :name, :id, :class_name) do
# Ensure these models match the interface required for exporting
@@ -42,39 +39,19 @@ module Timebox
alias_method :timebox_id, :id
- validates :group, presence: true, unless: :project
- validates :project, presence: true, unless: :group
-
- validate :timebox_type_check
validate :start_date_should_be_less_than_due_date, if: proc { |m| m.start_date.present? && m.due_date.present? }
validate :dates_within_4_digits
cache_markdown_field :title, pipeline: :single_line
cache_markdown_field :description, issuable_reference_expansion_enabled: true
- belongs_to :project
- belongs_to :group
-
has_many :issues
has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues
has_many :merge_requests
- scope :of_projects, ->(ids) { where(project_id: ids) }
- scope :of_groups, ->(ids) { where(group_id: ids) }
scope :closed, -> { with_state(:closed) }
- scope :for_projects, -> { where(group: nil).includes(:project) }
scope :with_title, -> (title) { where(title: title) }
- scope :for_projects_and_groups, -> (projects, groups) do
- projects = projects.compact if projects.is_a? Array
- projects = [] if projects.nil?
-
- groups = groups.compact if groups.is_a? Array
- groups = [] if groups.nil?
-
- from_union([where(project_id: projects), where(group_id: groups)], remove_duplicates: false)
- end
-
# A timebox is within the timeframe (start_date, end_date) if it overlaps
# with that timeframe:
#
@@ -132,10 +109,6 @@ module Timebox
end
end
- def count_by_state
- reorder(nil).group(:state).count
- end
-
def predefined_id?(id)
[Any.id, None.id, Upcoming.id, Started.id].include?(id)
end
@@ -145,29 +118,8 @@ module Timebox
end
end
- ##
- # Returns the String necessary to reference a Timebox in Markdown. Group
- # timeboxes only support name references, and do not support cross-project
- # references.
- #
- # format - Symbol format to use (default: :iid, optional: :name)
- #
- # Examples:
- #
- # Milestone.first.to_reference # => "%1"
- # Iteration.first.to_reference(format: :name) # => "*iteration:\"goal\""
- # Milestone.first.to_reference(cross_namespace_project) # => "gitlab-org/gitlab-foss%1"
- # Iteration.first.to_reference(same_namespace_project) # => "gitlab-foss*iteration:1"
- #
- def to_reference(from = nil, format: :name, full: false)
- format_reference = timebox_format_reference(format)
- reference = "#{self.class.reference_prefix}#{format_reference}"
-
- if project
- "#{project.to_reference_base(from, full: full)}#{reference}"
- else
- reference
- end
+ def to_reference
+ raise NotImplementedError
end
def reference_link_text(from = nil)
@@ -182,20 +134,12 @@ module Timebox
model_name.singular
end
- def group_timebox?
- group_id.present?
- end
-
- def project_timebox?
- project_id.present?
- end
-
def safe_title
title.to_slug.normalize.to_s
end
def resource_parent
- group || project
+ raise NotImplementedError
end
def to_ability_name
@@ -203,13 +147,7 @@ module Timebox
end
def merge_requests_enabled?
- if group_timebox?
- # Assume that groups have at least one project with merge requests enabled.
- # Otherwise, we would need to load all of the projects from the database.
- true
- elsif project_timebox?
- project&.merge_requests_enabled?
- end
+ raise NotImplementedError
end
def weight_available?
@@ -218,28 +156,6 @@ module Timebox
private
- def timebox_format_reference(format = :iid)
- raise ArgumentError, _('Unknown format') unless [:iid, :name].include?(format)
-
- if group_timebox? && format == :iid
- raise ArgumentError, _('Cannot refer to a group %{timebox_type} by an internal id!') % { timebox_type: timebox_name }
- end
-
- if format == :name && !name.include?('"')
- %("#{name}")
- else
- iid
- end
- end
-
- # Timebox should be either a project timebox or a group timebox
- def timebox_type_check
- if group_id && project_id
- field = project_id_changed? ? :project_id : :group_id
- errors.add(field, _("%{timebox_name} should belong either to a project or a group.") % { timebox_name: timebox_name })
- end
- end
-
def start_date_should_be_less_than_due_date
if due_date <= start_date
errors.add(:due_date, _("must be greater than start date"))
diff --git a/app/models/iteration.rb b/app/models/iteration.rb
index 71ecbcf1c1a..ed73793c78f 100644
--- a/app/models/iteration.rb
+++ b/app/models/iteration.rb
@@ -2,6 +2,12 @@
# Placeholder class for model that is implemented in EE
class Iteration < ApplicationRecord
+ include IgnorableColumns
+
+ # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/372125
+ # TODO https://gitlab.com/gitlab-org/gitlab/-/issues/372126
+ ignore_column :project_id, remove_with: '15.6', remove_after: '2022-09-17'
+
self.table_name = 'sprints'
def self.reference_prefix
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index ff4fadb0f13..da07d8dd9fc 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -1,11 +1,13 @@
# frozen_string_literal: true
class Milestone < ApplicationRecord
+ include AtomicInternalId
include Sortable
include Timebox
include Milestoneish
include FromUnion
include Importable
+ include IidRoutes
prepend_mod_with('Milestone') # rubocop: disable Cop/InjectEnterpriseEditionModule
@@ -13,6 +15,9 @@ class Milestone < ApplicationRecord
ALL = [::Timebox::None, ::Timebox::Any, ::Timebox::Started, ::Timebox::Upcoming].freeze
end
+ belongs_to :project
+ belongs_to :group
+
has_many :milestone_releases
has_many :releases, through: :milestone_releases
@@ -30,13 +35,28 @@ class Milestone < ApplicationRecord
.order(:project_id, :group_id, :due_date)
end
+ scope :of_projects, ->(ids) { where(project_id: ids) }
+ scope :for_projects, -> { where(group: nil).includes(:project) }
+ scope :for_projects_and_groups, -> (projects, groups) do
+ projects = projects.compact if projects.is_a? Array
+ projects = [] if projects.nil?
+
+ groups = groups.compact if groups.is_a? Array
+ groups = [] if groups.nil?
+
+ from_union([where(project_id: projects), where(group_id: groups)], remove_duplicates: false)
+ end
+
scope :order_by_name_asc, -> { order(Arel::Nodes::Ascending.new(arel_table[:title].lower)) }
scope :reorder_by_due_date_asc, -> { reorder(arel_table[:due_date].asc.nulls_last) }
scope :with_api_entity_associations, -> { preload(project: [:project_feature, :route, namespace: :route]) }
scope :order_by_dates_and_title, -> { order(due_date: :asc, start_date: :asc, title: :asc) }
+ validates :group, presence: true, unless: :project
+ validates :project, presence: true, unless: :group
validates :title, presence: true
validates_associated :milestone_releases, message: -> (_, obj) { obj[:value].map(&:errors).map(&:full_messages).join(",") }
+ validate :parent_type_check
validate :uniqueness_of_title, if: :title_changed?
state_machine :state, initial: :active do
@@ -176,10 +196,18 @@ class Milestone < ApplicationRecord
# TODO: remove after all code paths use `timebox_id`
# https://gitlab.com/gitlab-org/gitlab/-/issues/215688
alias_method :milestoneish_id, :timebox_id
- # TODO: remove after all code paths use (group|project)_timebox?
- # https://gitlab.com/gitlab-org/gitlab/-/issues/215690
- alias_method :group_milestone?, :group_timebox?
- alias_method :project_milestone?, :project_timebox?
+
+ def group_milestone?
+ group_id.present?
+ end
+
+ def project_milestone?
+ project_id.present?
+ end
+
+ def resource_parent
+ group || project
+ end
def parent
if group_milestone?
@@ -193,8 +221,63 @@ class Milestone < ApplicationRecord
group_milestone? && parent.subgroup?
end
+ def merge_requests_enabled?
+ if group_milestone?
+ # Assume that groups have at least one project with merge requests enabled.
+ # Otherwise, we would need to load all of the projects from the database.
+ true
+ elsif project_milestone?
+ project&.merge_requests_enabled?
+ end
+ end
+
+ ##
+ # Returns the String necessary to reference a milestone in Markdown. Group
+ # milestones only support name references, and do not support cross-project
+ # references.
+ #
+ # format - Symbol format to use (default: :iid, optional: :name)
+ #
+ # Examples:
+ #
+ # Milestone.first.to_reference # => "%1"
+ # Milestone.first.to_reference(cross_namespace_project) # => "gitlab-org/gitlab-foss%1"
+ #
+ def to_reference(from = nil, format: :name, full: false)
+ format_reference = timebox_format_reference(format)
+ reference = "#{self.class.reference_prefix}#{format_reference}"
+
+ if project
+ "#{project.to_reference_base(from, full: full)}#{reference}"
+ else
+ reference
+ end
+ end
+
private
+ def timebox_format_reference(format = :iid)
+ raise ArgumentError, _('Unknown format') unless [:iid, :name].include?(format)
+
+ if group_milestone? && format == :iid
+ raise ArgumentError, _('Cannot refer to a group milestone by an internal id!')
+ end
+
+ if format == :name && !name.include?('"')
+ %("#{name}")
+ else
+ iid
+ end
+ end
+
+ # Milestone should be either a project milestone or a group milestone
+ def parent_type_check
+ return unless group_id && project_id
+
+ field = project_id_changed? ? :project_id : :group_id
+ errors.add(field, _("milestone should belong either to a project or a group.") % { timebox_name: timebox_name })
+ end
+
def issues_finder_params
{ project_id: project_id, group_id: group_id, include_subgroups: group_id.present? }.compact
end
diff --git a/app/models/project.rb b/app/models/project.rb
index fcf7f71aa75..88cecc4770d 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -263,7 +263,6 @@ class Project < ApplicationRecord
has_many :integrations
has_many :events
has_many :milestones
- has_many :iterations
# Projects with a very large number of notes may time out destroying them
# through the foreign key. Additionally, the deprecated attachment uploader
diff --git a/app/models/user_detail.rb b/app/models/user_detail.rb
index b9b69d12729..3787ad1c380 100644
--- a/app/models/user_detail.rb
+++ b/app/models/user_detail.rb
@@ -2,9 +2,6 @@
class UserDetail < ApplicationRecord
extend ::Gitlab::Utils::Override
- include IgnorableColumns
-
- ignore_columns :other_role, remove_after: '2022-07-22', remove_with: '15.3'
REGISTRATION_OBJECTIVE_PAIRS = { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5, joining_team: 6 }.freeze
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index 6cefd9169f5..a848053ecaf 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -58,6 +58,7 @@ module MergeRequests
new_reviewers = merge_request.reviewers - old_reviewers
merge_request_activity_counter.track_users_review_requested(users: new_reviewers)
merge_request_activity_counter.track_reviewers_changed_action(user: current_user)
+ GraphqlTriggers.merge_request_reviewers_updated(merge_request)
end
def cleanup_environments(merge_request)
diff --git a/config/feature_flags/development/operational_vulnerabilities_filters.yml b/config/feature_flags/development/operational_vulnerabilities_filters.yml
index 0a96954ef66..93206935a94 100644
--- a/config/feature_flags/development/operational_vulnerabilities_filters.yml
+++ b/config/feature_flags/development/operational_vulnerabilities_filters.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/365986
milestone: '15.2'
type: development
group: group::container security
-default_enabled: false
+default_enabled: true
diff --git a/config/feature_flags/development/scan_execution_rule_mode.yml b/config/feature_flags/development/scan_execution_rule_mode.yml
index 44c985d6814..6e3dd6acf92 100644
--- a/config/feature_flags/development/scan_execution_rule_mode.yml
+++ b/config/feature_flags/development/scan_execution_rule_mode.yml
@@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359883
milestone: '15.2'
type: development
group: group::container security
-default_enabled: false
+default_enabled: true
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 61f7a38d272..56f1b7f7d7e 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -125,13 +125,16 @@ GET /groups?custom_attributes[key]=value&custom_attributes[other_key]=other_valu
## List a group's subgroups
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/15142) in GitLab 10.3.
-
Get a list of visible direct subgroups in this group.
-When accessed without authentication, only public groups are returned.
By default, this request returns 20 results at a time because the API results [are paginated](index.md#pagination).
+If you request this list as:
+
+- An unauthenticated user, the response returns only public groups.
+- An authenticated user, the response returns only the groups you're
+a member of and does not include public groups.
+
Parameters:
| Attribute | Type | Required | Description |
diff --git a/doc/user/application_security/generate_test_vulnerabilities/index.md b/doc/user/application_security/generate_test_vulnerabilities/index.md
index 11efedabff8..90cfc6f2147 100644
--- a/doc/user/application_security/generate_test_vulnerabilities/index.md
+++ b/doc/user/application_security/generate_test_vulnerabilities/index.md
@@ -7,22 +7,26 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Generate test vulnerabilities
-You can generate test vulnerabilities when you work on the [Vulnerability Report](../vulnerability_report/index.md).
+You can generate test vulnerabilities for the [Vulnerability Report](../vulnerability_report/index.md) to test GitLab
+vulnerability management features without running a pipeline.
+1. Login in to GitLab.
1. Go to `/-/profile/personal_access_tokens` and generate a personal access token with `api` permissions.
1. Go to your project page and find the project ID. You can find the project ID below the project title.
-1. Open a terminal and go to the `gitlab/qa` directory.
+1. [Clone the GitLab repository](../../../gitlab-basics/start-using-git.md#clone-a-repository) to your local machine.
+1. Open a terminal and go to `gitlab/qa` directory.
+1. Run `bundle install`
1. Run the following command:
```shell
-GITLAB_QA_ACCESS_TOKEN=<your_personal_access_token> GITLAB_URL="http://localhost:3000" bundle exec rake vulnerabilities:setup\[<your_project_id>,<vulnerability_count>\] --trace
+GITLAB_QA_ACCESS_TOKEN=<your_personal_access_token> GITLAB_URL="<address:port>" bundle exec rake vulnerabilities:setup\[<your_project_id>,<vulnerability_count>\] --trace
```
Make sure you do the following:
- Replace `<your_personal_access_token>` with the token you generated in step one.
-- Double check the `GITLAB_URL`. It should point to the running local instance.
-- Replace `<your_project_id>` with the ID you obtained in step two.
+- Double check the `GITLAB_URL`. It should point to address and port of your GitLab instance, for example `http://localhost:3000` if you are running GDK
+- Replace `<your_project_id>` with the ID you obtained in step three above.
- Replace `<vulnerability_count>` with the number of vulnerabilities you'd like to generate.
-The script creates the specified amount of vulnerabilities in the project.
+The script creates the specified number of placeholder vulnerabilities in the project.
diff --git a/doc/user/project/insights/index.md b/doc/user/project/insights/index.md
index 1107e91c33e..8e08d82c067 100644
--- a/doc/user/project/insights/index.md
+++ b/doc/user/project/insights/index.md
@@ -95,8 +95,9 @@ The following table lists the chart parameters:
### `title`
-`title` is the title of the chart as it displays on the Insights page.
-For example:
+Use `title` to update the chart title. The title displays on the insights report.
+
+**Example:**
```yaml
monthlyBugsCreated:
@@ -105,8 +106,9 @@ monthlyBugsCreated:
### `description`
-The `description` text is displayed above the chart, but below the title. It's used
-to give extra details regarding the chart, for example:
+Use `description` to add a description of the chart. The description displays above the chart, below the title.
+
+**Example:**
```yaml
monthlyBugsCreated:
@@ -116,33 +118,32 @@ monthlyBugsCreated:
### `type`
-`type` is the chart type.
+Use `type` to define the chart type.
-For example:
+**Supported values:**
-```yaml
-monthlyBugsCreated:
- title: "Monthly bugs created"
- type: bar
-```
-
-Supported values are:
-
-| Name | Example |
+| Name | Example: |
| ----- | ------- |
| `bar` | ![Insights example bar chart](img/insights_example_bar_chart.png) |
| `bar` (time series, that is when `group_by` is used) | ![Insights example bar time series chart](img/insights_example_bar_time_series_chart.png) |
| `line` | ![Insights example stacked bar chart](img/insights_example_line_chart.png) |
| `stacked-bar` | ![Insights example stacked bar chart](img/insights_example_stacked_bar_chart.png) |
-NOTE:
-The `dora` data source supports the `bar` and `line` chart types.
+The `dora` data source supports the `bar` and `line` [chart types](#type).
+
+**Example:**
+
+```yaml
+monthlyBugsCreated:
+ title: "Monthly bugs created"
+ type: bar
+```
### `query`
-`query` allows to define the data source and various filtering conditions for the chart.
+Use `query` to define the data source and filtering conditions for the chart.
-Example:
+**Example:**
```yaml
monthlyBugsCreated:
@@ -190,46 +191,46 @@ monthlyBugsCreated:
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/725) in GitLab 15.3.
-The `data_source` parameter was introduced to allow visualizing data from different data sources.
+Use `data_source` to define the data source that exposes the data.
-Supported values are:
+**Supported values:**
- `issuables`: Exposes merge request or issue data.
-- `dora`: Exposes DORA metrics data.
+- `dora`: Exposes DORA metrics.
-#### `Issuable` query parameters
+#### `issuable` query parameters
##### `query.params.issuable_type`
-Defines the type of "issuable" you want to create a chart for.
+Use `query.params.issuable_type` to define the type of issuable to create a chart for.
-Supported values are:
+**Supported values:**
- `issue`: The chart displays issues' data.
- `merge_request`: The chart displays merge requests' data.
##### `query.params.issuable_state`
-Filter by the current state of the queried "issuable".
+Use `query.params.issuable_state` to filter by the current state of the queried issuable.
By default, the `opened` state filter is applied.
-Supported values are:
+**Supported values:**
-- `opened`: Open issues / merge requests.
-- `closed`: Closed Open issues / merge requests.
-- `locked`: Issues / merge requests that have their discussion locked.
+- `opened`: Open issues or merge requests.
+- `closed`: Closed issues or merge requests.
+- `locked`: Issues or merge requests that have their discussion locked.
- `merged`: Merged merge requests.
-- `all`: Issues / merge requests in all states
+- `all`: Issues or merge requests in all states.
##### `query.params.filter_labels`
-Filter by labels currently applied to the queried "issuable".
+Use `query.params.filter_labels` to filter by labels applied to the queried issuable.
-By default, no labels filter is applied. All the defined labels must be
-currently applied to the "issuable" in order for it to be selected.
+By default, no label filter is applied. All defined labels must
+be applied to the issuable for it to be selected.
-Example:
+**Example:**:
```yaml
monthlyBugsCreated:
@@ -247,12 +248,13 @@ monthlyBugsCreated:
##### `query.params.collection_labels`
-Group "issuable" by the configured labels.
+Use `query.params.collection_labels` to group issuables by the configured labels.
+Grouping is not applied by default.
-By default, no grouping is done. When using this keyword, you need to
-set `type` to either `line` or `stacked-bar`.
+When using this parameter, you must
+set `type` to `line` or `stacked-bar`.
-Example:
+**Example:**
```yaml
weeklyBugsBySeverity:
@@ -274,9 +276,9 @@ weeklyBugsBySeverity:
##### `query.group_by`
-Define the X-axis of your chart.
+Use `query.group_by` to define the X-axis of the chart.
-Supported values are:
+**Supported values:**
- `day`: Group data per day.
- `week`: Group data per week.
@@ -284,11 +286,10 @@ Supported values are:
##### `query.period_limit`
-Define how far "issuables" are queried in the past (using the `query.period_field`).
+Use `query.period_limit` to define how far back in time to query issuables (using the `query.period_field`).
-The unit is related to the `query.group_by` you defined. For instance if you
-defined `query.group_by: 'day'` then `query.period_limit: 365` would mean
-"Gather and display data for the last 365 days".
+The unit is related to the value defined in `query.group_by`. For example, if you
+defined `query.group_by: 'day'`, and `query.period_limit: 365`, the chart displays data from the last 365 days.
By default, default values are applied depending on the `query.group_by`
you defined.
@@ -301,9 +302,9 @@ you defined.
#### `query.period_field`
-Define the timestamp field used to group "issuables".
+Use `query.period_field` to define the timestamp field by which to group issuables.
-Supported values are:
+**Supported values:**
- `created_at` (default): Group data using the `created_at` field.
- `closed_at`: Group data using the `closed_at` field (for issues only).
@@ -323,7 +324,9 @@ you may see `created_at` in place of `merged_at`. `created_at` is used instead.
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/367248) in GitLab 15.3.
-An example DORA chart definition:
+Use DORA-specific queries with the `dora` data source to create a DORA chart definition.
+
+**Example:**
```yaml
dora:
@@ -355,53 +358,59 @@ dora:
##### `query.metric`
-Defines which DORA metric to query. The available values are:
+Use `query.metric` to define the [DORA metrics](../../../api/dora/metrics.md#the-value-field) to query.
+
+**Supported values:**
- `deployment_frequency` (default)
- `lead_time_for_changes`
- `time_to_restore_service`
- `change_failure_rate`
-The metrics are described on the [DORA API](../../../api/dora/metrics.md#the-value-field) page.
-
##### `query.group_by`
-Define the X-axis of your chart.
+Use `query.group_by` to define the X-axis of your chart.
-Supported values are:
+**Supported values:**
- `day` (default): Group data per day.
- `month`: Group data per month.
##### `query.period_limit`
-Define how far the metrics are queried in the past (default: 15). Maximum lookback period is 180 days or 6 months.
+Use `query.period_limit` to define how far the metrics are queried in the past (default: 15). The maximum period is 180 days or 6 months.
##### `query.environment_tiers`
-An array of environments to include into the calculation (default: production). Available options: `production`, `staging`, `testing`, `development`, `other`.
+Use `query.environment_tiers` to define an array of environments to include the calculation.
+
+**Supported values:**
+
+- `production`(default)
+- `staging`
+- `testing`
+- `development`
+- `other`
### `projects`
-You can limit where the "issuables" can be queried from:
+Use `projects` to limit where issuables are queried from:
-- If `.gitlab/insights.yml` is used for a [group's insights](../../group/insights/index.md#configure-your-insights), with `projects`, you can limit the projects to be queried. By default, all projects currently under the group are used.
-- If `.gitlab/insights.yml` is used for a project's insights, specifying any other projects yields no results. By default, the project itself is used.
+- If `.gitlab/insights.yml` is used for a [group's insights](../../group/insights/index.md#configure-your-insights), use `projects` to define the projects from which to query issuables. By default, all projects under the group are used.
+- If `.gitlab/insights.yml` is used for a project's insights, specifying other projects does not yield results. By default, the project is used.
#### `projects.only`
-The `projects.only` option specifies the projects which the "issuables"
-should be queried from.
+Use `projects.only` to specify the projects from which issuables
+are queried.
-Projects listed here are ignored when:
+Projects listed in this parameter are ignored when:
- They don't exist.
- The current user doesn't have sufficient permissions to read them.
-- They are outside of the group.
+- They are outside the group.
-In the following `insights.yml` example, we specify the projects
-the queries are used on. This example is useful when setting
-a group's insights:
+**Example:**
```yaml
monthlyBugsCreated:
@@ -423,7 +432,7 @@ monthlyBugsCreated:
- groupB/project # Projects outside the group will be ignored
```
-## Complete example
+## Complete insights configuration example
```yaml
.projectsOnly: &projectsOnly
diff --git a/jest.config.base.js b/jest.config.base.js
index b631c2009d9..a6c22a8877c 100644
--- a/jest.config.base.js
+++ b/jest.config.base.js
@@ -174,7 +174,7 @@ module.exports = (path, options = {}) => {
restoreMocks: true,
slowTestThreshold: process.env.CI ? 6000 : 500,
transform: {
- '^.+\\.(gql|graphql)$': 'jest-transform-graphql',
+ '^.+\\.(gql|graphql)$': './spec/frontend/__helpers__/graphql_transformer.js',
'^.+_worker\\.js$': './spec/frontend/__helpers__/web_worker_transformer.js',
'^.+\\.js$': 'babel-jest',
'^.+\\.vue$': '@vue/vue2-jest',
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f291aa69443..291644836db 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -1101,9 +1101,6 @@ msgstr ""
msgid "%{text} is available"
msgstr ""
-msgid "%{timebox_name} should belong either to a project or a group."
-msgstr ""
-
msgid "%{timebox_type} does not support burnup charts"
msgstr ""
@@ -7578,7 +7575,7 @@ msgstr ""
msgid "Cannot promote issue due to insufficient permissions."
msgstr ""
-msgid "Cannot refer to a group %{timebox_type} by an internal id!"
+msgid "Cannot refer to a group milestone by an internal id!"
msgstr ""
msgid "Cannot show preview. For previews on sketch files, they must have the file format introduced by Sketch version 43 and above."
@@ -47595,9 +47592,6 @@ msgstr ""
msgid "is not allowed. Please use your regular email address."
msgstr ""
-msgid "is not allowed. We do not currently support project-level iterations"
-msgstr ""
-
msgid "is not in the group enforcing Group Managed Account"
msgstr ""
@@ -47730,6 +47724,9 @@ msgstr ""
msgid "metric_id must be unique across a project"
msgstr ""
+msgid "milestone should belong either to a project or a group."
+msgstr ""
+
msgid "missing"
msgstr ""
diff --git a/package.json b/package.json
index 4d13824ae9c..9083678d9db 100644
--- a/package.json
+++ b/package.json
@@ -236,7 +236,6 @@
"jest-jasmine2": "^27.5.1",
"jest-junit": "^12.0.0",
"jest-raw-loader": "^1.0.1",
- "jest-transform-graphql": "^2.1.0",
"jest-util": "^27.5.1",
"jsonlint": "^1.6.3",
"markdownlint-cli": "0.32.2",
diff --git a/spec/features/issues/related_issues_spec.rb b/spec/features/issues/related_issues_spec.rb
index 818e99f2ec9..62127295a7c 100644
--- a/spec/features/issues/related_issues_spec.rb
+++ b/spec/features/issues/related_issues_spec.rb
@@ -3,42 +3,41 @@
require 'spec_helper'
RSpec.describe 'Related issues', :js do
- let(:user) { create(:user) }
- let(:project) { create(:project_empty_repo, :public) }
- let(:project_b) { create(:project_empty_repo, :public) }
- let(:project_unauthorized) { create(:project_empty_repo, :public) }
- let(:issue_a) { create(:issue, project: project) }
- let(:issue_b) { create(:issue, project: project) }
- let(:issue_c) { create(:issue, project: project) }
- let(:issue_d) { create(:issue, project: project) }
- let(:issue_project_b_a) { create(:issue, project: project_b) }
- let(:issue_project_unauthorized_a) { create(:issue, project: project_unauthorized) }
+ let_it_be(:user) { create(:user) }
+
+ let_it_be(:project) { create(:project_empty_repo, :public) }
+ let_it_be(:project_b) { create(:project_empty_repo, :public) }
+ let_it_be(:project_unauthorized) { create(:project_empty_repo, :public) }
+ let_it_be(:internal_project) { create(:project_empty_repo, :internal) }
+ let_it_be(:private_project) { create(:project_empty_repo, :private) }
+ let_it_be(:public_project) { create(:project_empty_repo, :public) }
+
+ let_it_be(:issue_a) { create(:issue, project: project) }
+ let_it_be(:issue_b) { create(:issue, project: project) }
+ let_it_be(:issue_c) { create(:issue, project: project) }
+ let_it_be(:issue_d) { create(:issue, project: project) }
+ let_it_be(:issue_project_b_a) { create(:issue, project: project_b) }
+ let_it_be(:issue_project_unauthorized_a) { create(:issue, project: project_unauthorized) }
+ let_it_be(:internal_issue) { create(:issue, project: internal_project) }
+ let_it_be(:private_issue) { create(:issue, project: private_project) }
+ let_it_be(:public_issue) { create(:issue, project: public_project) }
context 'widget visibility' do
context 'when not logged in' do
it 'does not show widget when internal project' do
- project = create :project_empty_repo, :internal
- issue = create :issue, project: project
-
- visit project_issue_path(project, issue)
+ visit project_issue_path(internal_project, internal_issue)
expect(page).not_to have_css('.related-issues-block')
end
it 'does not show widget when private project' do
- project = create :project_empty_repo, :private
- issue = create :issue, project: project
-
- visit project_issue_path(project, issue)
+ visit project_issue_path(private_project, private_issue)
expect(page).not_to have_css('.related-issues-block')
end
it 'shows widget when public project' do
- project = create :project_empty_repo, :public
- issue = create :issue, project: project
-
- visit project_issue_path(project, issue)
+ visit project_issue_path(public_project, public_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
@@ -51,39 +50,29 @@ RSpec.describe 'Related issues', :js do
end
it 'shows widget when internal project' do
- project = create :project_empty_repo, :internal
- issue = create :issue, project: project
-
- visit project_issue_path(project, issue)
+ visit project_issue_path(internal_project, internal_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
end
it 'does not show widget when private project' do
- project = create :project_empty_repo, :private
- issue = create :issue, project: project
-
- visit project_issue_path(project, issue)
+ visit project_issue_path(private_project, private_issue)
expect(page).not_to have_css('.related-issues-block')
end
it 'shows widget when public project' do
- project = create :project_empty_repo, :public
- issue = create :issue, project: project
-
- visit project_issue_path(project, issue)
+ visit project_issue_path(public_project, public_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
end
it 'shows widget on their own public issue' do
- project = create :project_empty_repo, :public
- issue = create :issue, project: project, author: user
+ issue = create :issue, project: public_project, author: user
- visit project_issue_path(project, issue)
+ visit project_issue_path(public_project, issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
@@ -96,33 +85,27 @@ RSpec.describe 'Related issues', :js do
end
it 'shows widget when internal project' do
- project = create :project_empty_repo, :internal
- issue = create :issue, project: project
- project.add_guest(user)
+ internal_project.add_guest(user)
- visit project_issue_path(project, issue)
+ visit project_issue_path(internal_project, internal_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
end
it 'shows widget when private project' do
- project = create :project_empty_repo, :private
- issue = create :issue, project: project
- project.add_guest(user)
+ private_project.add_guest(user)
- visit project_issue_path(project, issue)
+ visit project_issue_path(private_project, private_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
end
it 'shows widget when public project' do
- project = create :project_empty_repo, :public
- issue = create :issue, project: project
- project.add_guest(user)
+ public_project.add_guest(user)
- visit project_issue_path(project, issue)
+ visit project_issue_path(public_project, public_issue)
expect(page).to have_css('.related-issues-block')
expect(page).not_to have_button 'Add a related issue'
@@ -135,44 +118,37 @@ RSpec.describe 'Related issues', :js do
end
it 'shows widget when internal project' do
- project = create :project_empty_repo, :internal
- issue = create :issue, project: project
- project.add_reporter(user)
+ internal_project.add_reporter(user)
- visit project_issue_path(project, issue)
+ visit project_issue_path(internal_project, internal_issue)
expect(page).to have_css('.related-issues-block')
expect(page).to have_button 'Add a related issue'
end
it 'shows widget when private project' do
- project = create :project_empty_repo, :private
- issue = create :issue, project: project
- project.add_reporter(user)
+ private_project.add_reporter(user)
- visit project_issue_path(project, issue)
+ visit project_issue_path(private_project, private_issue)
expect(page).to have_css('.related-issues-block')
expect(page).to have_button 'Add a related issue'
end
it 'shows widget when public project' do
- project = create :project_empty_repo, :public
- issue = create :issue, project: project
- project.add_reporter(user)
+ public_project.add_reporter(user)
- visit project_issue_path(project, issue)
+ visit project_issue_path(public_project, public_issue)
expect(page).to have_css('.related-issues-block')
expect(page).to have_button 'Add a related issue'
end
it 'shows widget on their own public issue' do
- project = create :project_empty_repo, :public
- issue = create :issue, project: project, author: user
- project.add_reporter(user)
+ issue = create :issue, project: public_project, author: user
+ public_project.add_reporter(user)
- visit project_issue_path(project, issue)
+ visit project_issue_path(public_project, issue)
expect(page).to have_css('.related-issues-block')
expect(page).to have_button 'Add a related issue'
@@ -184,8 +160,11 @@ RSpec.describe 'Related issues', :js do
let!(:issue_link_b) { create :issue_link, source: issue_a, target: issue_b }
let!(:issue_link_c) { create :issue_link, source: issue_a, target: issue_c }
- before do
+ before_all do
project.add_guest(user)
+ end
+
+ before do
sign_in(user)
end
@@ -213,9 +192,12 @@ RSpec.describe 'Related issues', :js do
end
context 'when user has permission to manage related issues' do
- before do
+ before_all do
project.add_maintainer(user)
project_b.add_maintainer(user)
+ end
+
+ before do
sign_in(user)
end
diff --git a/spec/features/projects/ci/lint_spec.rb b/spec/features/projects/ci/lint_spec.rb
index 608511ae5a5..8d5f62d8a06 100644
--- a/spec/features/projects/ci/lint_spec.rb
+++ b/spec/features/projects/ci/lint_spec.rb
@@ -5,13 +5,16 @@ require 'spec_helper'
RSpec.describe 'CI Lint', :js do
include Spec::Support::Helpers::Features::SourceEditorSpecHelpers
- let(:project) { create(:project, :repository) }
- let(:user) { create(:user) }
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { create(:user) }
let(:content_selector) { '.content .view-lines' }
- before do
+ before_all do
project.add_developer(user)
+ end
+
+ before do
sign_in(user)
visit project_ci_lint_path(project)
diff --git a/spec/frontend/__helpers__/graphql_transformer.js b/spec/frontend/__helpers__/graphql_transformer.js
new file mode 100644
index 00000000000..e776e2ea6ac
--- /dev/null
+++ b/spec/frontend/__helpers__/graphql_transformer.js
@@ -0,0 +1,8 @@
+/* eslint-disable import/no-commonjs */
+const loader = require('graphql-tag/loader');
+
+module.exports = {
+ process(src) {
+ return loader.call({ cacheable() {} }, src);
+ },
+};
diff --git a/spec/graphql/graphql_triggers_spec.rb b/spec/graphql/graphql_triggers_spec.rb
index 4d155a6c5f0..0de287370ba 100644
--- a/spec/graphql/graphql_triggers_spec.rb
+++ b/spec/graphql/graphql_triggers_spec.rb
@@ -75,4 +75,18 @@ RSpec.describe GraphqlTriggers do
GraphqlTriggers.issuable_dates_updated(work_item)
end
end
+
+ describe '.merge_request_reviewers_updated' do
+ it 'triggers the mergeRequestReviewersUpdated subscription' do
+ merge_request = build_stubbed(:merge_request)
+
+ expect(GitlabSchema.subscriptions).to receive(:trigger).with(
+ 'mergeRequestReviewersUpdated',
+ { issuable_id: merge_request.to_gid },
+ merge_request
+ ).and_call_original
+
+ GraphqlTriggers.merge_request_reviewers_updated(merge_request)
+ end
+ end
end
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index af1383b68bf..9f6b1f8016b 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -5,9 +5,35 @@ require 'spec_helper'
RSpec.describe Milestone do
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :public) }
+ let_it_be(:group) { create(:group) }
let_it_be(:issue) { create(:issue, project: project) }
+ describe 'modules' do
+ context 'with a project' do
+ it_behaves_like 'AtomicInternalId' do
+ let(:internal_id_attribute) { :iid }
+ let(:instance) { build(:milestone, project: create(:project), group: nil) }
+ let(:scope) { :project }
+ let(:scope_attrs) { { project: instance.project } }
+ let(:usage) { :milestones }
+ end
+ end
+
+ context 'with a group' do
+ it_behaves_like 'AtomicInternalId' do
+ let(:internal_id_attribute) { :iid }
+ let(:instance) { build(:milestone, project: nil, group: create(:group)) }
+ let(:scope) { :group }
+ let(:scope_attrs) { { namespace: instance.group } }
+ let(:usage) { :milestones }
+ end
+ end
+ end
+
it_behaves_like 'a timebox', :milestone do
+ let(:project) { create(:project, :public) }
+ let(:timebox) { create(:milestone, project: project) }
+
describe "#uniqueness_of_title" do
context "per project" do
it "does not accept the same title in a project twice" do
@@ -25,7 +51,7 @@ RSpec.describe Milestone do
end
context "per group" do
- let(:timebox) { create(:milestone, *timebox_args, group: group) }
+ let(:timebox) { create(:milestone, group: group) }
before do
project.update!(group: group)
@@ -96,9 +122,22 @@ RSpec.describe Milestone do
end
end
end
+
+ describe '#parent_type_check' do
+ let(:milestone) { build(:milestone, group: group) }
+
+ it 'is invalid if it has both project_id and group_id' do
+ milestone.project = project
+
+ expect(milestone).not_to be_valid
+ expect(milestone.errors[:project_id]).to include("milestone should belong either to a project or a group.")
+ end
+ end
end
describe "Associations" do
+ it { is_expected.to belong_to(:project) }
+ it { is_expected.to belong_to(:group) }
it { is_expected.to have_many(:releases) }
it { is_expected.to have_many(:milestone_releases) }
end
@@ -562,6 +601,57 @@ RSpec.describe Milestone do
it { is_expected.not_to match("gitlab-org/gitlab-ce/milestones/123") }
end
+ describe '#merge_requests_enabled?' do
+ context "per project" do
+ it "is true for projects with MRs enabled" do
+ project = create(:project, :merge_requests_enabled)
+ milestone = build(:milestone, project: project)
+
+ expect(milestone.merge_requests_enabled?).to be_truthy
+ end
+
+ it "is false for projects with MRs disabled" do
+ project = create(:project, :repository_enabled, :merge_requests_disabled)
+ milestone = build(:milestone, project: project)
+
+ expect(milestone.merge_requests_enabled?).to be_falsey
+ end
+
+ it "is false for projects with repository disabled" do
+ project = create(:project, :repository_disabled)
+ milestone = build(:milestone, project: project)
+
+ expect(milestone.merge_requests_enabled?).to be_falsey
+ end
+ end
+
+ context "per group" do
+ let(:milestone) { build(:milestone, group: group) }
+
+ it "is always true for groups, for performance reasons" do
+ expect(milestone.merge_requests_enabled?).to be_truthy
+ end
+ end
+ end
+
+ describe '#resource_parent' do
+ context 'when group is present' do
+ let(:milestone) { build(:milestone, group: group) }
+
+ it 'returns the group' do
+ expect(milestone.resource_parent).to eq(group)
+ end
+ end
+
+ context 'when project is present' do
+ let(:milestone) { build(:milestone, project: project) }
+
+ it 'returns the project' do
+ expect(milestone.resource_parent).to eq(project)
+ end
+ end
+ end
+
describe '#parent' do
context 'with group' do
it 'returns the expected parent' do
@@ -598,4 +688,40 @@ RSpec.describe Milestone do
end
end
end
+
+ describe '#project_milestone?' do
+ context 'when project_id is present' do
+ let(:milestone) { build(:milestone, project: project) }
+
+ it 'returns true' do
+ expect(milestone.project_milestone?).to be_truthy
+ end
+ end
+
+ context 'when project_id is not present' do
+ let(:milestone) { build(:milestone, group: group) }
+
+ it 'returns false' do
+ expect(milestone.project_milestone?).to be_falsey
+ end
+ end
+ end
+
+ describe '#group_milestone?' do
+ context 'when group_id is present' do
+ let(:milestone) { build(:milestone, group: group) }
+
+ it 'returns true' do
+ expect(milestone.group_milestone?).to be_truthy
+ end
+ end
+
+ context 'when group_id is not present' do
+ let(:milestone) { build(:milestone, project: project) }
+
+ it 'returns false' do
+ expect(milestone.group_milestone?).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 6c85c66c6eb..20e8245dc2e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -28,7 +28,6 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to have_many(:issues) }
it { is_expected.to have_many(:incident_management_issuable_escalation_statuses).through(:issues).inverse_of(:project).class_name('IncidentManagement::IssuableEscalationStatus') }
it { is_expected.to have_many(:milestones) }
- it { is_expected.to have_many(:iterations) }
it { is_expected.to have_many(:project_members).dependent(:delete_all) }
it { is_expected.to have_many(:users).through(:project_members) }
it { is_expected.to have_many(:requesters).dependent(:delete_all) }
diff --git a/spec/services/merge_requests/update_reviewers_service_spec.rb b/spec/services/merge_requests/update_reviewers_service_spec.rb
index 8920141adbb..96824ea6974 100644
--- a/spec/services/merge_requests/update_reviewers_service_spec.rb
+++ b/spec/services/merge_requests/update_reviewers_service_spec.rb
@@ -128,6 +128,12 @@ RSpec.describe MergeRequests::UpdateReviewersService do
set_reviewers
end
+ it 'triggers graphql subscription mergeRequestReviewersUpdated' do
+ expect(GraphqlTriggers).to receive(:merge_request_reviewers_updated).with(merge_request)
+
+ set_reviewers
+ end
+
it 'calls MergeRequest::ResolveTodosService#async_execute' do
expect_next_instance_of(MergeRequests::ResolveTodosService, merge_request, user) do |service|
expect(service).to receive(:async_execute)
@@ -149,6 +155,16 @@ RSpec.describe MergeRequests::UpdateReviewersService do
set_reviewers
end
+ context 'when reviewers did not change' do
+ let(:opts) { { reviewer_ids: merge_request.reviewer_ids } }
+
+ it 'does not trigger graphql subscription mergeRequestReviewersUpdated' do
+ expect(GraphqlTriggers).not_to receive(:merge_request_reviewers_updated)
+
+ set_reviewers
+ end
+ end
+
it 'does not update the reviewers if they do not have access' do
opts[:reviewer_ids] = [create(:user).id]
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 5d76fb051f2..473b7fa8f17 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -711,6 +711,20 @@ RSpec.describe MergeRequests::UpdateService, :mailer do
expect(user2.review_requested_open_merge_requests_count).to eq(1)
expect(user3.review_requested_open_merge_requests_count).to eq(0)
end
+
+ it 'triggers graphql subscription mergeRequestReviewersUpdated' do
+ expect(GraphqlTriggers).to receive(:merge_request_reviewers_updated).with(merge_request)
+
+ update_merge_request({ reviewer_ids: [user2.id] })
+ end
+ end
+
+ context 'when reviewers did not change' do
+ it 'does not trigger graphql subscription mergeRequestReviewersUpdated' do
+ expect(GraphqlTriggers).not_to receive(:merge_request_reviewers_updated)
+
+ update_merge_request({ reviewer_ids: [merge_request.reviewer_ids] })
+ end
end
context 'when the milestone is removed' do
diff --git a/spec/support/rspec_order_todo.yml b/spec/support/rspec_order_todo.yml
index d2e9313f660..b3b2376c1a8 100644
--- a/spec/support/rspec_order_todo.yml
+++ b/spec/support/rspec_order_todo.yml
@@ -3263,12 +3263,10 @@
- './ee/spec/services/users_ops_dashboard_projects/destroy_service_spec.rb'
- './ee/spec/services/users/update_highest_member_role_service_spec.rb'
- './ee/spec/services/vulnerabilities/confirm_service_spec.rb'
-- './ee/spec/services/vulnerabilities/create_from_security_finding_service_spec.rb'
- './ee/spec/services/vulnerabilities/create_service_spec.rb'
- './ee/spec/services/vulnerabilities/destroy_dismissal_feedback_service_spec.rb'
- './ee/spec/services/vulnerabilities/dismiss_service_spec.rb'
- './ee/spec/services/vulnerabilities/finding_dismiss_service_spec.rb'
-- './ee/spec/services/vulnerabilities/findings/create_from_security_finding_service_spec.rb'
- './ee/spec/services/vulnerabilities/historical_statistics/adjustment_service_spec.rb'
- './ee/spec/services/vulnerabilities/historical_statistics/deletion_service_spec.rb'
- './ee/spec/services/vulnerabilities/manually_create_service_spec.rb'
diff --git a/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb b/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
index d06e8391a9a..e4958779957 100644
--- a/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
+++ b/spec/support/shared_examples/models/concerns/timebox_shared_examples.rb
@@ -1,10 +1,7 @@
# frozen_string_literal: true
RSpec.shared_examples 'a timebox' do |timebox_type|
- let(:project) { create(:project, :public) }
- let(:group) { create(:group) }
let(:timebox_args) { [] }
- let(:timebox) { create(timebox_type, *timebox_args, project: project) }
let(:issue) { create(:issue, project: project) }
let(:user) { create(:user) }
let(:timebox_table_name) { timebox_type.to_s.pluralize.to_sym }
@@ -14,28 +11,6 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
let(:open_on_left) { nil }
let(:open_on_right) { nil }
- describe 'modules' do
- context 'with a project' do
- it_behaves_like 'AtomicInternalId' do
- let(:internal_id_attribute) { :iid }
- let(:instance) { build(timebox_type, *timebox_args, project: create(:project), group: nil) }
- let(:scope) { :project }
- let(:scope_attrs) { { project: instance.project } }
- let(:usage) { timebox_table_name }
- end
- end
-
- context 'with a group' do
- it_behaves_like 'AtomicInternalId' do
- let(:internal_id_attribute) { :iid }
- let(:instance) { build(timebox_type, *timebox_args, project: nil, group: create(:group)) }
- let(:scope) { :group }
- let(:scope_attrs) { { namespace: instance.group } }
- let(:usage) { timebox_table_name }
- end
- end
- end
-
describe "Validation" do
before do
allow(subject).to receive(:set_iid).and_return(false)
@@ -65,21 +40,9 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
expect(timebox.errors[:due_date]).to include("date must not be after 9999-12-31")
end
end
-
- describe '#timebox_type_check' do
- it 'is invalid if it has both project_id and group_id' do
- timebox = build(timebox_type, *timebox_args, group: group)
- timebox.project = project
-
- expect(timebox).not_to be_valid
- expect(timebox.errors[:project_id]).to include("#{timebox_type} should belong either to a project or a group.")
- end
- end
end
describe "Associations" do
- it { is_expected.to belong_to(:project) }
- it { is_expected.to belong_to(:group) }
it { is_expected.to have_many(:issues) }
it { is_expected.to have_many(:merge_requests) }
it { is_expected.to have_many(:labels).through(:issues) }
@@ -91,38 +54,6 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
end
end
- describe '#project_timebox?' do
- context 'when project_id is present' do
- it 'returns true' do
- expect(timebox.project_timebox?).to be_truthy
- end
- end
-
- context 'when project_id is not present' do
- let(:timebox) { build(timebox_type, *timebox_args, group: group) }
-
- it 'returns false' do
- expect(timebox.project_timebox?).to be_falsey
- end
- end
- end
-
- describe '#group_timebox?' do
- context 'when group_id is present' do
- let(:timebox) { build(timebox_type, *timebox_args, group: group) }
-
- it 'returns true' do
- expect(timebox.group_timebox?).to be_truthy
- end
- end
-
- context 'when group_id is not present' do
- it 'returns false' do
- expect(timebox.group_timebox?).to be_falsey
- end
- end
- end
-
describe '#safe_title' do
let(:timebox) { create(timebox_type, *timebox_args, title: "<b>foo & bar -> 2.2</b>") }
@@ -131,22 +62,6 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
end
end
- describe '#resource_parent' do
- context 'when group is present' do
- let(:timebox) { build(timebox_type, *timebox_args, group: group) }
-
- it 'returns the group' do
- expect(timebox.resource_parent).to eq(group)
- end
- end
-
- context 'when project is present' do
- it 'returns the project' do
- expect(timebox.resource_parent).to eq(project)
- end
- end
- end
-
describe "#title" do
let(:timebox) { create(timebox_type, *timebox_args, title: "<b>foo & bar -> 2.2</b>") }
@@ -155,39 +70,6 @@ RSpec.shared_examples 'a timebox' do |timebox_type|
end
end
- describe '#merge_requests_enabled?' do
- context "per project" do
- it "is true for projects with MRs enabled" do
- project = create(:project, :merge_requests_enabled)
- timebox = create(timebox_type, *timebox_args, project: project)
-
- expect(timebox.merge_requests_enabled?).to be_truthy
- end
-
- it "is false for projects with MRs disabled" do
- project = create(:project, :repository_enabled, :merge_requests_disabled)
- timebox = create(timebox_type, *timebox_args, project: project)
-
- expect(timebox.merge_requests_enabled?).to be_falsey
- end
-
- it "is false for projects with repository disabled" do
- project = create(:project, :repository_disabled)
- timebox = create(timebox_type, *timebox_args, project: project)
-
- expect(timebox.merge_requests_enabled?).to be_falsey
- end
- end
-
- context "per group" do
- let(:timebox) { create(timebox_type, *timebox_args, group: group) }
-
- it "is always true for groups, for performance reasons" do
- expect(timebox.merge_requests_enabled?).to be_truthy
- end
- end
- end
-
describe '#to_ability_name' do
it 'returns timebox' do
timebox = build(timebox_type, *timebox_args)
diff --git a/yarn.lock b/yarn.lock
index 0614b2285a3..a6b5192a8ae 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7578,11 +7578,6 @@ jest-snapshot@^27.5.1:
pretty-format "^27.5.1"
semver "^7.3.2"
-jest-transform-graphql@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/jest-transform-graphql/-/jest-transform-graphql-2.1.0.tgz#903cb66bb27bc2772fd3e5dd4f7e9b57230f5829"
- integrity sha1-kDy2a7J7wncv0+XdT36bVyMPWCk=
-
jest-util@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9"