Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-12-06 09:09:20 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-06 09:09:20 +0300
commitf713e4eb11cae4593cfe70554a4a23536ab91357 (patch)
tree5da1a55d3eb2a9d26712c8c76dfdf713a09680e6
parent43c0fa568b3e3689abd3d57eb058433f33c9df1c (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/pipeline_wizard/components/step_nav.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/awards_list.vue5
-rw-r--r--app/graphql/graphql_triggers.rb8
-rw-r--r--app/models/merge_request.rb6
-rw-r--r--app/models/project.rb23
-rw-r--r--app/serializers/merge_request_poll_widget_entity.rb2
-rw-r--r--app/services/merge_requests/approval_service.rb1
-rw-r--r--app/services/merge_requests/base_service.rb4
-rw-r--r--app/services/merge_requests/remove_approval_service.rb1
-rw-r--r--app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml2
-rw-r--r--app/views/admin/groups/_group.html.haml6
-rw-r--r--app/views/dashboard/_groups_head.html.haml4
-rw-r--r--app/views/groups/_new_group_fields.html.haml6
-rw-r--r--app/views/groups/settings/_export.html.haml12
-rw-r--r--doc/user/application_security/dast/browser_based.md91
-rw-r--r--spec/features/projects/import_export/export_file_spec.rb2
-rw-r--r--spec/features/projects/import_export/import_file_spec.rb2
-rw-r--r--spec/features/projects/integrations/disable_triggers_spec.rb2
-rw-r--r--spec/features/projects/integrations/project_integrations_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_asana_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_assembla_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_emails_on_push_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_flowdock_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_irker_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_jira_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_packagist_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_prometheus_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_pushover_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_slack_notifications_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_uses_inherited_settings_spec.rb2
-rw-r--r--spec/features/projects/integrations/user_views_services_spec.rb2
-rw-r--r--spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap305
-rw-r--r--spec/frontend/vue_shared/components/awards_list_spec.js49
-rw-r--r--spec/graphql/graphql_triggers_spec.rb14
-rw-r--r--spec/models/project_spec.rb64
-rw-r--r--spec/services/merge_requests/approval_service_spec.rb8
-rw-r--r--spec/services/merge_requests/remove_approval_service_spec.rb8
-rw-r--r--spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb17
-rw-r--r--vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb25
44 files changed, 313 insertions, 393 deletions
diff --git a/app/assets/javascripts/pipeline_wizard/components/step_nav.vue b/app/assets/javascripts/pipeline_wizard/components/step_nav.vue
index 8f9198855c6..e3d825bbcc7 100644
--- a/app/assets/javascripts/pipeline_wizard/components/step_nav.vue
+++ b/app/assets/javascripts/pipeline_wizard/components/step_nav.vue
@@ -27,12 +27,13 @@ export default {
</script>
<template>
- <div>
+ <div class="gl-display-flex">
<slot name="before"></slot>
<gl-button
v-if="showBackButton"
category="secondary"
data-testid="back-button"
+ class="gl-mr-3"
@click="$emit('back')"
>
{{ __('Back') }}
diff --git a/app/assets/javascripts/vue_shared/components/awards_list.vue b/app/assets/javascripts/vue_shared/components/awards_list.vue
index da4b21a2790..cb38b3e13bb 100644
--- a/app/assets/javascripts/vue_shared/components/awards_list.vue
+++ b/app/assets/javascripts/vue_shared/components/awards_list.vue
@@ -159,10 +159,7 @@ export default {
return;
}
- // 100 and 1234 emoji are a number. Callback for v-for click sends it as a string
- const parsedName = /^[0-9]+$/.test(awardName) ? Number(awardName) : awardName;
-
- this.$emit('award', parsedName);
+ this.$emit('award', awardName);
if (document.activeElement) document.activeElement.blur();
},
diff --git a/app/graphql/graphql_triggers.rb b/app/graphql/graphql_triggers.rb
index 710e7fe110c..7f83b62a2ff 100644
--- a/app/graphql/graphql_triggers.rb
+++ b/app/graphql/graphql_triggers.rb
@@ -44,6 +44,14 @@ module GraphqlTriggers
merge_request
)
end
+
+ def self.merge_request_approval_state_updated(merge_request)
+ GitlabSchema.subscriptions.trigger(
+ 'mergeRequestApprovalStateUpdated',
+ { issuable_id: merge_request.to_gid },
+ merge_request
+ )
+ end
end
GraphqlTriggers.prepend_mod
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 03993506d80..ce95b152fb7 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1271,7 +1271,7 @@ class MergeRequest < ApplicationRecord
end
def mergeable_discussions_state?
- return true unless project.only_allow_merge_if_all_discussions_are_resolved?
+ return true unless project.only_allow_merge_if_all_discussions_are_resolved?(inherit_group_setting: true)
unresolved_notes.none?(&:to_be_resolved?)
end
@@ -1452,9 +1452,9 @@ class MergeRequest < ApplicationRecord
end
def mergeable_ci_state?
- return true unless project.only_allow_merge_if_pipeline_succeeds?
+ return true unless project.only_allow_merge_if_pipeline_succeeds?(inherit_group_setting: true)
return false unless actual_head_pipeline
- return true if project.allow_merge_on_skipped_pipeline? && actual_head_pipeline.skipped?
+ return true if project.allow_merge_on_skipped_pipeline?(inherit_group_setting: true) && actual_head_pipeline.skipped?
actual_head_pipeline.success?
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 4cb8ada8ea1..8d66c0d6ea5 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -742,6 +742,29 @@ class Project < ApplicationRecord
end
end
+ # Defines instance methods:
+ #
+ # - only_allow_merge_if_pipeline_succeeds?(inherit_group_setting: false)
+ # - allow_merge_on_skipped_pipeline?(inherit_group_setting: false)
+ # - only_allow_merge_if_all_discussions_are_resolved?(inherit_group_setting: false)
+ # - only_allow_merge_if_pipeline_succeeds_locked?
+ # - allow_merge_on_skipped_pipeline_locked?
+ # - only_allow_merge_if_all_discussions_are_resolved_locked?
+ def self.cascading_with_parent_namespace(attribute)
+ # method overriden in EE
+ define_method("#{attribute}?") do |inherit_group_setting: false|
+ self.public_send(attribute) # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ define_method("#{attribute}_locked?") do
+ false
+ end
+ end
+
+ cascading_with_parent_namespace :only_allow_merge_if_pipeline_succeeds
+ cascading_with_parent_namespace :allow_merge_on_skipped_pipeline
+ cascading_with_parent_namespace :only_allow_merge_if_all_discussions_are_resolved
+
def self.with_feature_available_for_user(feature, user)
with_project_feature.merge(ProjectFeature.with_feature_available_for_user(feature, user))
end
diff --git a/app/serializers/merge_request_poll_widget_entity.rb b/app/serializers/merge_request_poll_widget_entity.rb
index ab180b35b29..cef3f4555df 100644
--- a/app/serializers/merge_request_poll_widget_entity.rb
+++ b/app/serializers/merge_request_poll_widget_entity.rb
@@ -31,7 +31,7 @@ class MergeRequestPollWidgetEntity < Grape::Entity
end
expose :only_allow_merge_if_pipeline_succeeds do |merge_request|
- merge_request.project.only_allow_merge_if_pipeline_succeeds?
+ merge_request.project.only_allow_merge_if_pipeline_succeeds?(inherit_group_setting: true)
end
# CI related
diff --git a/app/services/merge_requests/approval_service.rb b/app/services/merge_requests/approval_service.rb
index 72f398ce415..8560a15b7c4 100644
--- a/app/services/merge_requests/approval_service.rb
+++ b/app/services/merge_requests/approval_service.rb
@@ -13,6 +13,7 @@ module MergeRequests
merge_request_activity_counter.track_approve_mr_action(user: current_user, merge_request: merge_request)
trigger_merge_request_merge_status_updated(merge_request)
trigger_merge_request_reviewers_updated(merge_request)
+ trigger_merge_request_approval_state_updated(merge_request)
# Approval side effects (things not required to be done immediately but
# should happen after a successful approval) should be done asynchronously
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index fabf006979b..cccba5add13 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -262,6 +262,10 @@ module MergeRequests
GraphqlTriggers.merge_request_merge_status_updated(merge_request)
end
+ def trigger_merge_request_approval_state_updated(merge_request)
+ GraphqlTriggers.merge_request_approval_state_updated(merge_request)
+ end
+
def capture_suggested_reviewers_accepted(merge_request)
# Implemented in EE
end
diff --git a/app/services/merge_requests/remove_approval_service.rb b/app/services/merge_requests/remove_approval_service.rb
index 8387c23fe3f..c0bb257eda6 100644
--- a/app/services/merge_requests/remove_approval_service.rb
+++ b/app/services/merge_requests/remove_approval_service.rb
@@ -19,6 +19,7 @@ module MergeRequests
merge_request_activity_counter.track_unapprove_mr_action(user: current_user)
trigger_merge_request_merge_status_updated(merge_request)
trigger_merge_request_reviewers_updated(merge_request)
+ trigger_merge_request_approval_state_updated(merge_request)
end
success
diff --git a/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml b/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml
index 415606c055d..d7bb3a85f3a 100644
--- a/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml
+++ b/app/views/admin/application_settings/appearances/_system_header_footer_form.html.haml
@@ -20,7 +20,7 @@
label_options: { class: 'gl-font-weight-bold!' }
.form-group.js-toggle-colors-container
- %button.btn.gl-button.btn-link.js-toggle-colors-link{ type: 'button' }
+ = render Pajamas::ButtonComponent.new(variant: :link, button_options: { class: 'js-toggle-colors-link' }) do
= _('Customize colors')
.form-group.js-toggle-colors-container.hide
= form.label :message_background_color, _('Background Color'), class: 'col-form-label label-bold'
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index a1afb1ddbfa..f9ebda2bc21 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -31,5 +31,7 @@
= visibility_level_icon(group.visibility_level)
.controls.gl-flex-shrink-0.gl-ml-5
- = link_to _('Edit'), admin_group_edit_path(group), id: "edit_#{dom_id(group)}", class: 'btn gl-button btn-default'
- = link_to _('Delete'), [:admin, group], aria: { label: _('Remove') }, data: { confirm: _("Are you sure you want to remove %{group_name}?") % { group_name: group.name }, confirm_btn_variant: 'danger' }, method: :delete, class: 'gl-button btn btn-danger'
+ = render Pajamas::ButtonComponent.new(href: admin_group_edit_path(group), button_options: { id: "edit_#{dom_id(group)}" }) do
+ = _('Edit')
+ = render Pajamas::ButtonComponent.new(href: [:admin, group], variant: :danger, button_options: { data: { confirm: _("Are you sure you want to remove %{group_name}?") % { group_name: group.name }, confirm_btn_variant: 'danger', method: :delete } }) do
+ = _('Delete')
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index 1c82b30ed8d..09e2e35c617 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -3,8 +3,8 @@
- if current_user.can_create_group?
.page-title-controls
- = link_to _("New group"), new_group_path, class: "gl-button btn btn-confirm", data: { qa_selector: "new_group_button", testid: "new-group-button" }
-
+ = render Pajamas::ButtonComponent.new(href: new_group_path, variant: :confirm, button_options: { data: { qa_selector: "new_group_button", testid: "new-group-button" } }) do
+ = _("New group")
.top-area
= gl_tabs_nav({ class: 'gl-flex-grow-1 gl-border-0' }) do
= gl_tab_link_to _("Your groups"), dashboard_groups_path
diff --git a/app/views/groups/_new_group_fields.html.haml b/app/views/groups/_new_group_fields.html.haml
index 94b0b018084..95990e8937c 100644
--- a/app/views/groups/_new_group_fields.html.haml
+++ b/app/views/groups/_new_group_fields.html.haml
@@ -30,5 +30,7 @@
= recaptcha_tags nonce: content_security_policy_nonce
.row
.col-sm-12
- = f.submit submit_label, class: "btn gl-button btn-confirm", data: { qa_selector: 'create_group_button' }
- = link_to _('Cancel'), dashboard_groups_path, class: 'btn gl-button btn-default btn-cancel'
+ = f.submit submit_label, pajamas_button: true, data: { qa_selector: 'create_group_button' }
+ = render Pajamas::ButtonComponent.new(href: dashboard_groups_path) do
+ = _('Cancel')
+
diff --git a/app/views/groups/settings/_export.html.haml b/app/views/groups/settings/_export.html.haml
index 66c1341fb15..5d79d0f8e79 100644
--- a/app/views/groups/settings/_export.html.haml
+++ b/app/views/groups/settings/_export.html.haml
@@ -25,10 +25,10 @@
%li= _('Runner tokens')
%li= _('SAML discovery tokens')
- if group.export_file_exists?
- = link_to _('Download export'), download_export_group_path(group),
- rel: 'nofollow', method: :get, class: 'btn gl-button btn-default', data: { qa_selector: 'download_export_link' }
- = link_to _('Regenerate export'), export_group_path(group),
- method: :post, class: 'btn gl-button btn-default', data: { qa_selector: 'regenerate_export_group_link' }
+ = render Pajamas::ButtonComponent.new(href: download_export_group_path(group), button_options: { rel: 'nofollow', data: { method: :get, qa_selector: 'download_export_link' } }) do
+ = _('Download export')
+ = render Pajamas::ButtonComponent.new(href: export_group_path(group), button_options: { data: { method: :post, qa_selector: 'regenerate_export_group_link' } }) do
+ = _('Regenerate export')
- else
- = link_to _('Export group'), export_group_path(group),
- method: :post, class: 'btn gl-button btn-default', data: { qa_selector: 'export_group_link' }
+ = render Pajamas::ButtonComponent.new(href: export_group_path(group), button_options: { data: { method: :post, qa_selector: 'export_group_link' } }) do
+ = _('Export group')
diff --git a/doc/user/application_security/dast/browser_based.md b/doc/user/application_security/dast/browser_based.md
index 909490fa6e5..a79dceb3ce5 100644
--- a/doc/user/application_security/dast/browser_based.md
+++ b/doc/user/application_security/dast/browser_based.md
@@ -12,24 +12,79 @@ type: reference, howto
WARNING:
This product is in an early-access stage and is considered a [beta](../../../policy/alpha-beta-support.md#beta-features) feature.
-GitLab DAST's browser-based analyzer was built by GitLab to test Single Page Applications (SPAs) and
-traditional web applications. It both crawls the web application and analyzes the resulting output
-for vulnerabilities. Analysis of modern applications, heavily reliant on JavaScript, is vital to
-ensuring DAST coverage.
-
-The browser-based scanner works by loading the target application into a specially-instrumented
-Chromium browser. A snapshot of the page is taken before a search to find any actions that a user
-might perform, such as selecting on a link or filling in a form. For each action found, the
-browser-based scanner executes it, takes a new snapshot, and determines what in the page changed
-from the previous snapshot. Crawling continues by taking more snapshots and finding subsequent
-actions. The benefit of scanning by following user actions in a browser is that the crawler can
-interact with the target application much like a real user would, identifying complex flows that
-traditional web crawlers don't understand. This results in better coverage of the website.
-
-The browser-based scanner should provide greater coverage for most web applications, compared
-with the current DAST AJAX crawler. While both crawlers are
-used together with the current DAST scanner, the combination of the browser-based crawler with the
-current DAST scanner is much more effective at finding and testing every page in an application.
+WARNING:
+Do not run DAST scans against a production server. Not only can it perform *any* function that
+a user can, such as clicking buttons or submitting forms, but it may also trigger bugs, leading to modification or loss of production data. Only run DAST scans against a test server.
+
+The DAST browser-based analyzer was built by GitLab to scan modern-day web applications for vulnerabilities.
+Scans run in a browser to optimize testing applications heavily dependent on JavaScript, such as single-page applications.
+See [how DAST scans an application](#how-dast-scans-an-application) for more information.
+
+To add the analyzer to your CI/CD pipeline, see [enable browser-based analyzer](#enable-browser-based-analyzer).
+
+## How DAST scans an application
+
+A scan performs the following steps:
+
+1. [Authenticate](authentication.md), if configured.
+1. [Crawl](#crawling-an-application) the target application to discover the surface area of the application by performing user actions such as following links, clicking buttons, and filling out forms.
+1. [Passive scan](#passive-scans) to search for vulnerabilities in HTTP messages and pages discovered while crawling.
+1. [Active scan](#active-scans) to search for vulnerabilities by injecting payloads into HTTP requests recorded during the crawl phase.
+
+### Crawling an application
+
+A "navigation" is an action a user might take on a page, such as clicking buttons, clicking anchor links, opening menu items, or filling out forms.
+A "navigation path" is a sequence of navigation actions representing how a user might traverse an application.
+DAST discovers the surface area of an application by crawling pages and content and identifying navigation paths.
+
+Crawling is initialized with a navigation path containing one navigation that loads the target application URL in a specially-instrumented Chromium browser.
+DAST then crawls navigation paths until all have been crawled.
+
+To crawl a navigation path, DAST opens a browser window and instructs it to perform all the navigation actions in the navigation path.
+When the browser has finished loading the result of the final action, DAST inspects the page for actions a user might take,
+creates a new navigation for each found, and adds them to the navigation path to form new navigation paths. For example:
+
+- DAST processes navigation path `LoadURL[https://example.com]`.
+- DAST finds two user actions, `LeftClick[class=menu]` and `LeftClick[id=users]`.
+- DAST creates two new navigation paths, `LoadURL[https://example.com] -> LeftClick[class=menu]` and `LoadURL[https://example.com] -> LeftClick[id=users]`.
+- Crawling begins on the two new navigation paths.
+
+It's common for an HTML element to exist in multiple places in an application, such as a menu visible on every page.
+Duplicate elements can cause crawlers to crawl the same pages again or become stuck in a loop.
+DAST uses an element uniqueness calculation based on HTML attributes to discard new navigation actions it has previously crawled.
+
+### Passive scans
+
+Passive scans check for vulnerabilities in the pages discovered during the crawl phase of the scan.
+Passive scans are enabled by default.
+
+The checks search HTTP messages, cookies, storage events, console events, and DOM for vulnerabilities.
+Examples of passive checks include searching for exposed credit cards, exposed secret tokens, missing content security policies, and redirection to untrusted locations.
+
+See [checks](checks/index.md) for more information about individual checks.
+
+### Active scans
+
+Active scans check for vulnerabilities by injecting attack payloads into HTTP requests recorded during the crawl phase of the scan.
+Active scans are disabled by default due to the nature of their probing attacks.
+
+DAST analyzes each recorded HTTP request for injection locations, such as query values, header values, cookie values, form posts, and JSON string values.
+Attack payloads are injected into the injection location, forming a new request.
+DAST sends the request to the target application and uses the HTTP response to determine attack success.
+
+Active scans run two types of active check:
+
+- A match response attack analyzes the response content to determine attack success. For example, if an attack attempts to read the system password file, a finding is created when the response body contains evidence of the password file.
+- A timing attack uses the response time to determine attack success. For example, if an attack attempts to force the target application to sleep, a finding is created when the application takes longer to respond than the sleep time. Timing attacks are repeated multiple times with different attack payloads to minimize false positives.
+
+A simplified timing attack works as follows:
+
+1. The crawl phase records the HTTP request `https://example.com?search=people`.
+1. DAST analyzes the URL and finds a URL parameter injection location `https://example.com?search=[INJECT]`.
+1. The active check defines a payload, `sleep 10`, that attempts to get a Linux host to sleep.
+1. DAST send a new HTTP request to the target application with the injected payload `https://example.com?search=sleep%2010`.
+1. The target application is vulnerable if it executes the query parameter value as a system command without validation, for example, `system(params[:search])`
+1. DAST creates a finding if the response time takes longer than 10 seconds.
## Enable browser-based analyzer
diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb
index ccf3ccc6a96..f8012242480 100644
--- a/spec/features/projects/import_export/export_file_spec.rb
+++ b/spec/features/projects/import_export/export_file_spec.rb
@@ -6,7 +6,7 @@ require 'spec_helper'
# It looks up for any sensitive word inside the JSON, so if a sensitive word is found
# we'll have to either include it adding the model that includes it to the +safe_list+
# or make sure the attribute is blacklisted in the +import_export.yml+ configuration
-RSpec.describe 'Import/Export - project export integration test', :js do
+RSpec.describe 'Import/Export - project export integration test', :js, feature_category: :importers do
include Select2Helper
include ExportFileHelper
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index 6f015f9cd22..8fb11f06cdd 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Import/Export - project import integration test', :js do
+RSpec.describe 'Import/Export - project import integration test', :js, feature_category: :importers do
let(:user) { create(:user) }
let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
let(:export_path) { "#{Dir.tmpdir}/import_file_spec" }
diff --git a/spec/features/projects/integrations/disable_triggers_spec.rb b/spec/features/projects/integrations/disable_triggers_spec.rb
index b039d610ecb..f7afce6d87c 100644
--- a/spec/features/projects/integrations/disable_triggers_spec.rb
+++ b/spec/features/projects/integrations/disable_triggers_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Disable individual triggers', :js do
+RSpec.describe 'Disable individual triggers', :js, feature_category: :integrations do
include_context 'project integration activation'
let(:checkbox_selector) { 'input[name$="_events]"]' }
diff --git a/spec/features/projects/integrations/project_integrations_spec.rb b/spec/features/projects/integrations/project_integrations_spec.rb
index 708a5bca8c1..d99b6ca9092 100644
--- a/spec/features/projects/integrations/project_integrations_spec.rb
+++ b/spec/features/projects/integrations/project_integrations_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Project integrations', :js do
+RSpec.describe 'Project integrations', :js, feature_category: :integrations do
include_context 'project integration activation'
it_behaves_like 'integration settings form' do
diff --git a/spec/features/projects/integrations/user_activates_asana_spec.rb b/spec/features/projects/integrations/user_activates_asana_spec.rb
index 9ec9f00529a..b99ca2ebc1c 100644
--- a/spec/features/projects/integrations/user_activates_asana_spec.rb
+++ b/spec/features/projects/integrations/user_activates_asana_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Asana' do
+RSpec.describe 'User activates Asana', feature_category: :integrations do
include_context 'project integration activation'
it 'activates integration', :js do
diff --git a/spec/features/projects/integrations/user_activates_assembla_spec.rb b/spec/features/projects/integrations/user_activates_assembla_spec.rb
index be9034ec5ba..db5774e4514 100644
--- a/spec/features/projects/integrations/user_activates_assembla_spec.rb
+++ b/spec/features/projects/integrations/user_activates_assembla_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Assembla' do
+RSpec.describe 'User activates Assembla', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb b/spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb
index 49f62a34bd2..a532c1b8644 100644
--- a/spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb
+++ b/spec/features/projects/integrations/user_activates_atlassian_bamboo_ci_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Atlassian Bamboo CI' do
+RSpec.describe 'User activates Atlassian Bamboo CI', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_emails_on_push_spec.rb b/spec/features/projects/integrations/user_activates_emails_on_push_spec.rb
index 168779aad07..9a2d693a9f0 100644
--- a/spec/features/projects/integrations/user_activates_emails_on_push_spec.rb
+++ b/spec/features/projects/integrations/user_activates_emails_on_push_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Emails on push' do
+RSpec.describe 'User activates Emails on push', feature_category: :integrations do
include_context 'project integration activation'
it 'activates integration', :js do
diff --git a/spec/features/projects/integrations/user_activates_flowdock_spec.rb b/spec/features/projects/integrations/user_activates_flowdock_spec.rb
index df1a4feddfb..9786ea9a692 100644
--- a/spec/features/projects/integrations/user_activates_flowdock_spec.rb
+++ b/spec/features/projects/integrations/user_activates_flowdock_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Flowdock' do
+RSpec.describe 'User activates Flowdock', feature_category: :integrations do
include_context 'project integration activation' do
let(:project) { create(:project, :repository) }
end
diff --git a/spec/features/projects/integrations/user_activates_irker_spec.rb b/spec/features/projects/integrations/user_activates_irker_spec.rb
index 23b5f2a5c47..17c46bfaff7 100644
--- a/spec/features/projects/integrations/user_activates_irker_spec.rb
+++ b/spec/features/projects/integrations/user_activates_irker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates irker (IRC gateway)' do
+RSpec.describe 'User activates irker (IRC gateway)', feature_category: :integrations do
include_context 'project integration activation'
it 'activates integration', :js do
diff --git a/spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb b/spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb
index f86a1b8a0a4..a18c052beb9 100644
--- a/spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb
+++ b/spec/features/projects/integrations/user_activates_jetbrains_teamcity_ci_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates JetBrains TeamCity CI' do
+RSpec.describe 'User activates JetBrains TeamCity CI', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_jira_spec.rb b/spec/features/projects/integrations/user_activates_jira_spec.rb
index dad201ffbb6..e4b10aeb340 100644
--- a/spec/features/projects/integrations/user_activates_jira_spec.rb
+++ b/spec/features/projects/integrations/user_activates_jira_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Jira', :js do
+RSpec.describe 'User activates Jira', :js, feature_category: :integrations do
include_context 'project integration activation'
include_context 'project integration Jira context'
diff --git a/spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb b/spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb
index 54c9ec0f62e..16c7a3ff226 100644
--- a/spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb
+++ b/spec/features/projects/integrations/user_activates_mattermost_slash_command_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Set up Mattermost slash commands', :js do
+RSpec.describe 'Set up Mattermost slash commands', :js, feature_category: :integrations do
describe 'user visits the mattermost slash command config page' do
include_context 'project integration activation'
diff --git a/spec/features/projects/integrations/user_activates_packagist_spec.rb b/spec/features/projects/integrations/user_activates_packagist_spec.rb
index 0892843e840..2d77abfea7c 100644
--- a/spec/features/projects/integrations/user_activates_packagist_spec.rb
+++ b/spec/features/projects/integrations/user_activates_packagist_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Packagist' do
+RSpec.describe 'User activates Packagist', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb b/spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb
index fe6ed786ace..b4dec8ffdb5 100644
--- a/spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb
+++ b/spec/features/projects/integrations/user_activates_pivotaltracker_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates PivotalTracker' do
+RSpec.describe 'User activates PivotalTracker', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_prometheus_spec.rb b/spec/features/projects/integrations/user_activates_prometheus_spec.rb
index 56b895919b8..5b2d885410f 100644
--- a/spec/features/projects/integrations/user_activates_prometheus_spec.rb
+++ b/spec/features/projects/integrations/user_activates_prometheus_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Prometheus' do
+RSpec.describe 'User activates Prometheus', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_pushover_spec.rb b/spec/features/projects/integrations/user_activates_pushover_spec.rb
index 616efdc836f..a705c354a1e 100644
--- a/spec/features/projects/integrations/user_activates_pushover_spec.rb
+++ b/spec/features/projects/integrations/user_activates_pushover_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Pushover' do
+RSpec.describe 'User activates Pushover', feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb b/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb
index e89f6e309ea..01c202baf70 100644
--- a/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb
+++ b/spec/features/projects/integrations/user_activates_slack_notifications_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User activates Slack notifications', :js do
+RSpec.describe 'User activates Slack notifications', :js, feature_category: :integrations do
include_context 'project integration activation'
context 'when integration is not configured yet' do
diff --git a/spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb b/spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb
index df8cd84ffdb..0f6d721565e 100644
--- a/spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb
+++ b/spec/features/projects/integrations/user_activates_slack_slash_command_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'Slack slash commands', :js do
+RSpec.describe 'Slack slash commands', :js, feature_category: :integrations do
include_context 'project integration activation'
before do
diff --git a/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb b/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb
index 8a2881c95dc..e0063a9c733 100644
--- a/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb
+++ b/spec/features/projects/integrations/user_uses_inherited_settings_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User uses inherited settings', :js do
+RSpec.describe 'User uses inherited settings', :js, feature_category: :integrations do
include JiraIntegrationHelpers
include_context 'project integration activation'
diff --git a/spec/features/projects/integrations/user_views_services_spec.rb b/spec/features/projects/integrations/user_views_services_spec.rb
index 559461f911f..e6be300c0a9 100644
--- a/spec/features/projects/integrations/user_views_services_spec.rb
+++ b/spec/features/projects/integrations/user_views_services_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe 'User views integrations', :js do
+RSpec.describe 'User views integrations', :js, feature_category: :integrations do
include_context 'project integration activation'
it 'shows the list of available integrations' do
diff --git a/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap b/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap
deleted file mode 100644
index 06be40fc941..00000000000
--- a/spec/frontend/vue_shared/components/__snapshots__/awards_list_spec.js.snap
+++ /dev/null
@@ -1,305 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`vue_shared/components/awards_list default matches snapshot 1`] = `
-<div
- class="awards js-awards-block"
->
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button"
- data-testid="award-button"
- title="Ada, Leonardo, and Marie reacted with :thumbsup:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="thumbsup"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 3
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button selected"
- data-testid="award-button"
- title="You, Ada, and Marie reacted with :thumbsdown:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="thumbsdown"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 3
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button"
- data-testid="award-button"
- title="Ada and Jane reacted with :smile:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="smile"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 2
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button selected"
- data-testid="award-button"
- title="You, Ada, Jane, and Leonardo reacted with :ok_hand:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="ok_hand"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 4
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button selected"
- data-testid="award-button"
- title="You reacted with :cactus:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="cactus"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 1
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button"
- data-testid="award-button"
- title="Marie reacted with :a:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="a"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 1
- </span>
- </span>
- </button>
- <button
- class="btn gl-mr-3 gl-my-2 btn-default btn-md gl-button selected"
- data-testid="award-button"
- title="You reacted with :b:"
- type="button"
- >
- <!---->
-
- <!---->
-
- <span
- class="award-emoji-block"
- data-testid="award-html"
- >
- <gl-emoji
- data-name="b"
- />
- </span>
-
- <span
- class="gl-button-text"
- >
-
- <span
- class="js-counter"
- >
- 1
- </span>
- </span>
- </button>
-
- <div
- class="award-menu-holder gl-my-2"
- >
- <div
- class="emoji-picker"
- data-testid="emoji-picker"
- title="Add reaction"
- >
- <div
- boundary="scrollParent"
- class="dropdown b-dropdown gl-dropdown btn-group"
- id="__BVID__13"
- lazy=""
- menu-class="dropdown-extended-height"
- no-flip=""
- >
- <!---->
- <button
- aria-expanded="false"
- aria-haspopup="true"
- class="btn dropdown-toggle btn-default btn-md add-reaction-button btn-icon gl-relative! gl-button gl-dropdown-toggle btn-default-secondary"
- id="__BVID__13__BV_toggle_"
- type="button"
- >
- <span
- class="gl-sr-only"
- >
- Add reaction
- </span>
-
- <span
- class="reaction-control-icon reaction-control-icon-neutral"
- >
- <svg
- aria-hidden="true"
- class="gl-icon s16"
- data-testid="slight-smile-icon"
- role="img"
- >
- <use
- href="#slight-smile"
- />
- </svg>
- </span>
-
- <span
- class="reaction-control-icon reaction-control-icon-positive"
- >
- <svg
- aria-hidden="true"
- class="gl-icon s16"
- data-testid="smiley-icon"
- role="img"
- >
- <use
- href="#smiley"
- />
- </svg>
- </span>
-
- <span
- class="reaction-control-icon reaction-control-icon-super-positive"
- >
- <svg
- aria-hidden="true"
- class="gl-icon s16"
- data-testid="smile-icon"
- role="img"
- >
- <use
- href="#smile"
- />
- </svg>
- </span>
- </button>
- <ul
- aria-labelledby="__BVID__13__BV_toggle_"
- class="dropdown-menu dropdown-extended-height dropdown-menu-right"
- role="menu"
- tabindex="-1"
- >
- <!---->
- </ul>
- </div>
- </div>
- </div>
-</div>
-`;
diff --git a/spec/frontend/vue_shared/components/awards_list_spec.js b/spec/frontend/vue_shared/components/awards_list_spec.js
index 1c8cf726aca..c7f9d8fd8d5 100644
--- a/spec/frontend/vue_shared/components/awards_list_spec.js
+++ b/spec/frontend/vue_shared/components/awards_list_spec.js
@@ -38,7 +38,18 @@ const TEST_AWARDS = [
createAward(EMOJI_CACTUS, USERS.root),
createAward(EMOJI_A, USERS.marie),
createAward(EMOJI_B, USERS.root),
+ createAward(EMOJI_100, USERS.ada),
];
+const TEST_AWARDS_LENGTH = [
+ EMOJI_SMILE,
+ EMOJI_OK,
+ EMOJI_THUMBSUP,
+ EMOJI_THUMBSDOWN,
+ EMOJI_A,
+ EMOJI_B,
+ EMOJI_CACTUS,
+ EMOJI_100,
+].length;
const TEST_ADD_BUTTON_CLASS = 'js-test-add-button-class';
const REACTION_CONTROL_CLASSES = [
@@ -88,10 +99,6 @@ describe('vue_shared/components/awards_list', () => {
});
});
- it('matches snapshot', () => {
- expect(wrapper.element).toMatchSnapshot();
- });
-
it('shows awards in correct order', () => {
expect(findAwardsData()).toEqual([
{
@@ -108,6 +115,12 @@ describe('vue_shared/components/awards_list', () => {
},
{
classes: REACTION_CONTROL_CLASSES,
+ count: 1,
+ html: matchingEmojiTag(EMOJI_100),
+ title: `Ada reacted with :${EMOJI_100}:`,
+ },
+ {
+ classes: REACTION_CONTROL_CLASSES,
count: 2,
html: matchingEmojiTag(EMOJI_SMILE),
title: `Ada and Jane reacted with :${EMOJI_SMILE}:`,
@@ -142,33 +155,23 @@ describe('vue_shared/components/awards_list', () => {
it('with award clicked, it emits award', () => {
expect(wrapper.emitted().award).toBeUndefined();
- findAwardButtons().at(2).vm.$emit('click');
+ findAwardButtons().at(3).vm.$emit('click');
expect(wrapper.emitted().award).toEqual([[EMOJI_SMILE]]);
});
- it('shows add award button', () => {
- const btn = findAddAwardButton();
+ it('with numeric award clicked, it emits award as is', () => {
+ expect(wrapper.emitted().award).toBeUndefined();
- expect(btn.exists()).toBe(true);
- });
- });
+ findAwardButtons().at(2).vm.$emit('click');
- describe('with numeric award', () => {
- beforeEach(() => {
- createComponent({
- awards: [createAward(EMOJI_100, USERS.ada)],
- canAwardEmoji: true,
- currentUserId: USERS.root.id,
- });
+ expect(wrapper.emitted().award).toEqual([[EMOJI_100]]);
});
- it('when clicked, it emits award as number', () => {
- expect(wrapper.emitted().award).toBeUndefined();
-
- findAwardButtons().at(0).vm.$emit('click');
+ it('shows add award button', () => {
+ const btn = findAddAwardButton();
- expect(wrapper.emitted().award).toEqual([[Number(EMOJI_100)]]);
+ expect(btn.exists()).toBe(true);
});
});
@@ -210,7 +213,7 @@ describe('vue_shared/components/awards_list', () => {
it('disables award buttons', () => {
const buttons = findAwardButtons();
- expect(buttons.length).toBe(7);
+ expect(buttons.length).toBe(TEST_AWARDS_LENGTH);
expect(buttons.wrappers.every((x) => x.classes('disabled'))).toBe(true);
});
});
diff --git a/spec/graphql/graphql_triggers_spec.rb b/spec/graphql/graphql_triggers_spec.rb
index a54cb8a7988..00b5aec366e 100644
--- a/spec/graphql/graphql_triggers_spec.rb
+++ b/spec/graphql/graphql_triggers_spec.rb
@@ -116,4 +116,18 @@ RSpec.describe GraphqlTriggers do
GraphqlTriggers.merge_request_merge_status_updated(merge_request)
end
end
+
+ describe '.merge_request_approval_state_updated' do
+ it 'triggers the mergeRequestApprovalStateUpdated subscription' do
+ merge_request = build_stubbed(:merge_request)
+
+ expect(GitlabSchema.subscriptions).to receive(:trigger).with(
+ 'mergeRequestApprovalStateUpdated',
+ { issuable_id: merge_request.to_gid },
+ merge_request
+ ).and_call_original
+
+ GraphqlTriggers.merge_request_approval_state_updated(merge_request)
+ end
+ end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 3af02340ded..a6dbaef3a66 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -8380,6 +8380,70 @@ RSpec.describe Project, factory_default: :keep do
it { is_expected.to be(false) }
end
+ describe '.cascading_with_parent_namespace' do
+ let_it_be_with_reload(:group) { create(:group, :with_root_storage_statistics) }
+ let_it_be_with_reload(:subgroup) { create(:group, parent: group) }
+ let_it_be_with_reload(:project) { create(:project, group: subgroup) }
+ let_it_be_with_reload(:project_without_group) { create(:project) }
+
+ shared_examples 'cascading settings' do |attribute|
+ it 'return self value when no parent' do
+ expect(project_without_group.group).to be_nil
+
+ project_without_group.update!(attribute => true)
+ expect(project_without_group.public_send("#{attribute}?", inherit_group_setting: true)).to be_truthy
+ expect(project_without_group.public_send("#{attribute}_locked?")).to be_falsey
+
+ project_without_group.update!(attribute => false)
+ expect(project_without_group.public_send("#{attribute}?", inherit_group_setting: true)).to be_falsey
+ expect(project_without_group.public_send("#{attribute}_locked?")).to be_falsey
+ end
+
+ it 'return self value when unlocked' do
+ subgroup.namespace_settings.update!(attribute => false)
+ group.namespace_settings.update!(attribute => false)
+
+ project.update!(attribute => true)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_truthy
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+
+ project.update!(attribute => false)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_falsey
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+ end
+
+ it 'still return self value when locked subgroup' do
+ subgroup.namespace_settings.update!(attribute => true)
+ group.namespace_settings.update!(attribute => false)
+
+ project.update!(attribute => true)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_truthy
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+
+ project.update!(attribute => false)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_falsey
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+ end
+
+ it 'still return unlocked value when locked group' do
+ subgroup.namespace_settings.update!(attribute => false)
+ group.namespace_settings.update!(attribute => true)
+
+ project.update!(attribute => true)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_truthy
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+
+ project.update!(attribute => false)
+ expect(project.public_send("#{attribute}?", inherit_group_setting: true)).to be_falsey
+ expect(project.public_send("#{attribute}_locked?")).to be_falsey
+ end
+ end
+
+ it_behaves_like 'cascading settings', :only_allow_merge_if_pipeline_succeeds
+ it_behaves_like 'cascading settings', :allow_merge_on_skipped_pipeline
+ it_behaves_like 'cascading settings', :only_allow_merge_if_all_discussions_are_resolved
+ end
+
private
def finish_job(export_job)
diff --git a/spec/services/merge_requests/approval_service_spec.rb b/spec/services/merge_requests/approval_service_spec.rb
index da6492aca95..1d6427900b9 100644
--- a/spec/services/merge_requests/approval_service_spec.rb
+++ b/spec/services/merge_requests/approval_service_spec.rb
@@ -91,6 +91,10 @@ RSpec.describe MergeRequests::ApprovalService do
it_behaves_like 'triggers GraphQL subscription mergeRequestReviewersUpdated' do
let(:action) { service.execute(merge_request) }
end
+
+ it_behaves_like 'triggers GraphQL subscription mergeRequestApprovalStateUpdated' do
+ let(:action) { service.execute(merge_request) }
+ end
end
context 'user cannot update the merge request' do
@@ -109,6 +113,10 @@ RSpec.describe MergeRequests::ApprovalService do
it_behaves_like 'does not trigger GraphQL subscription mergeRequestReviewersUpdated' do
let(:action) { service.execute(merge_request) }
end
+
+ it_behaves_like 'does not trigger GraphQL subscription mergeRequestApprovalStateUpdated' do
+ let(:action) { service.execute(merge_request) }
+ end
end
end
end
diff --git a/spec/services/merge_requests/remove_approval_service_spec.rb b/spec/services/merge_requests/remove_approval_service_spec.rb
index 7b38f0d1c45..fd8240935e8 100644
--- a/spec/services/merge_requests/remove_approval_service_spec.rb
+++ b/spec/services/merge_requests/remove_approval_service_spec.rb
@@ -53,6 +53,10 @@ RSpec.describe MergeRequests::RemoveApprovalService do
it_behaves_like 'triggers GraphQL subscription mergeRequestReviewersUpdated' do
let(:action) { execute! }
end
+
+ it_behaves_like 'triggers GraphQL subscription mergeRequestApprovalStateUpdated' do
+ let(:action) { execute! }
+ end
end
context 'with a user who has not approved' do
@@ -77,6 +81,10 @@ RSpec.describe MergeRequests::RemoveApprovalService do
it_behaves_like 'does not trigger GraphQL subscription mergeRequestReviewersUpdated' do
let(:action) { execute! }
end
+
+ it_behaves_like 'does not trigger GraphQL subscription mergeRequestApprovalStateUpdated' do
+ let(:action) { execute! }
+ end
end
end
end
diff --git a/spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb b/spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb
new file mode 100644
index 00000000000..455fd308be8
--- /dev/null
+++ b/spec/support/shared_examples/services/approval_state_updated_trigger_shared_examples.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'triggers GraphQL subscription mergeRequestApprovalStateUpdated' do
+ specify do
+ expect(GraphqlTriggers).to receive(:merge_request_approval_state_updated).with(merge_request)
+
+ action
+ end
+end
+
+RSpec.shared_examples 'does not trigger GraphQL subscription mergeRequestApprovalStateUpdated' do
+ specify do
+ expect(GraphqlTriggers).not_to receive(:merge_request_approval_state_updated)
+
+ action
+ end
+end
diff --git a/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb
index fed0e11080f..47a9b676f1d 100644
--- a/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb
+++ b/vendor/gems/bundler-checksum/lib/bundler/checksum/command/init.rb
@@ -16,14 +16,22 @@ module Bundler::Checksum::Command
.send(:compact_index_client)
.instance_variable_get(:@cache)
- seen = []
Bundler.definition.resolve.sort_by(&:name).each do |spec|
next unless spec.source.is_a?(Bundler::Source::Rubygems)
+ spec_identifier = "#{spec.name}==#{spec.version}"
- next if seen.include?(spec.name)
- seen << spec.name
+ previous_checksum = previous_checksums.select do |checksum|
+ checksum[:name] == spec.name && checksum[:version] == spec.version.to_s
+ end
+
+ if !previous_checksum.empty?
+ $stderr.puts "Using #{spec_identifier}"
+ checksums += previous_checksum
+
+ next
+ end
- $stderr.puts "Adding #{spec.name}==#{spec.version}"
+ $stderr.puts "Adding #{spec_identifier}"
compact_index_dependencies = compact_index_cache.dependencies(spec.name).select { |item| item.first == spec.version.to_s }
@@ -54,6 +62,15 @@ module Bundler::Checksum::Command
private
+ def previous_checksums
+ @previous_checksums ||=
+ if File.exist?(checksum_file)
+ ::Bundler::Checksum.checksums_from_file
+ else
+ []
+ end
+ end
+
def checksum_file
::Bundler::Checksum.checksum_file
end