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:
-rw-r--r--.eslintrc.yml4
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue1
-rw-r--r--app/assets/javascripts/jobs/components/job_log_controllers.vue63
-rw-r--r--app/assets/stylesheets/framework/typography.scss6
-rw-r--r--app/assets/stylesheets/page_bundles/escalation_policies.scss16
-rw-r--r--app/assets/stylesheets/page_bundles/issues_show.scss73
-rw-r--r--app/controllers/projects/jobs_controller.rb5
-rw-r--r--config/feature_flags/development/job_log_jump_to_failures.yml8
-rw-r--r--doc/api/group_protected_environments.md2
-rw-r--r--doc/integration/saml.md1
-rw-r--r--doc/user/application_security/dependency_scanning/index.md2
-rw-r--r--doc/user/application_security/get-started-security.md34
-rw-r--r--lib/tasks/contracts/merge_requests.rake17
-rw-r--r--lib/tasks/contracts/pipeline_schedules.rake2
-rw-r--r--lib/tasks/contracts/pipelines.rake16
-rw-r--r--locale/gitlab.pot3
-rw-r--r--spec/contracts/consumer/endpoints/project/pipelines.js16
-rw-r--r--spec/contracts/consumer/fixtures/project/merge_request/diffs_batch.fixture.js10
-rw-r--r--spec/contracts/consumer/fixtures/project/merge_request/diffs_metadata.fixture.js10
-rw-r--r--spec/contracts/consumer/fixtures/project/merge_request/discussions.fixture.js10
-rw-r--r--spec/contracts/consumer/fixtures/project/pipeline/create_a_new_pipeline.fixture.js4
-rw-r--r--spec/contracts/consumer/fixtures/project/pipeline/delete_pipeline.fixture.js5
-rw-r--r--spec/contracts/consumer/fixtures/project/pipeline/get_list_project_pipelines.fixture.js10
-rw-r--r--spec/contracts/consumer/fixtures/project/pipeline/get_pipeline_header_data.fixture.js9
-rw-r--r--spec/contracts/consumer/fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture.js4
-rw-r--r--spec/contracts/consumer/resources/api/project/merge_requests.js (renamed from spec/contracts/consumer/endpoints/project/merge_requests.js)14
-rw-r--r--spec/contracts/consumer/resources/api/project/pipelines.js15
-rw-r--r--spec/contracts/consumer/specs/project/merge_request/show.spec.js36
-rw-r--r--spec/contracts/consumer/specs/project/pipeline/index.spec.js16
-rw-r--r--spec/contracts/consumer/specs/project/pipeline/new.spec.js4
-rw-r--r--spec/contracts/consumer/specs/project/pipeline/show.spec.js12
-rw-r--r--spec/contracts/consumer/specs/project/pipeline_schedule/edit.spec.js4
-rw-r--r--spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json4
-rw-r--r--spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_batch_helper.rb (renamed from spec/contracts/provider/pact_helpers/project/merge_request/diffs_batch_helper.rb)4
-rw-r--r--spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_metadata_helper.rb (renamed from spec/contracts/provider/pact_helpers/project/merge_request/diffs_metadata_helper.rb)4
-rw-r--r--spec/contracts/provider/pact_helpers/project/merge_request/show/discussions_helper.rb (renamed from spec/contracts/provider/pact_helpers/project/merge_request/discussions_helper.rb)4
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipeline/index/create_a_new_pipeline_helper.rb (renamed from spec/contracts/provider/pact_helpers/project/pipeline/create_a_new_pipeline_helper.rb)4
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipeline/index/get_list_project_pipelines_helper.rb (renamed from spec/contracts/provider/pact_helpers/project/pipeline/get_list_project_pipelines_helper.rb)4
-rw-r--r--spec/contracts/provider/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb2
-rw-r--r--spec/contracts/provider/states/project/merge_request/diffs_batch_state.rb18
-rw-r--r--spec/contracts/provider/states/project/merge_request/diffs_metadata_state.rb18
-rw-r--r--spec/contracts/provider/states/project/merge_request/discussions_state.rb17
-rw-r--r--spec/contracts/provider/states/project/merge_request/show_state.rb47
-rw-r--r--spec/contracts/provider/states/project/pipeline/index_state.rb (renamed from spec/contracts/provider/states/project/pipeline/pipelines_state.rb)0
-rw-r--r--spec/contracts/provider/states/project/pipeline/pipeline_state.rb27
-rw-r--r--spec/frontend/ide/stores/getters_spec.js6
-rw-r--r--spec/frontend/jobs/components/job_log_controllers_spec.js114
-rw-r--r--spec/frontend/monitoring/components/dashboard_actions_menu_spec.js2
48 files changed, 391 insertions, 316 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 22dae35187e..5e6755edaa5 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -177,3 +177,7 @@ overrides:
'@graphql-eslint/no-unused-variables': error
'@graphql-eslint/no-unused-fragments': error
'@graphql-eslint/no-duplicate-fields': error
+ - files:
+ - 'spec/contracts/consumer/**/*'
+ rules:
+ '@gitlab/require-i18n-strings': off
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
index f9e6c64aad1..d5ee3423d70 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -287,6 +287,7 @@ export default {
:is-scroll-top-disabled="isScrollTopDisabled"
:is-job-log-size-visible="isJobLogSizeVisible"
:is-scrolling-down="isScrollingDown"
+ :is-complete="isJobLogComplete"
:job-log="jobLog"
@scrollJobLogTop="scrollTop"
@scrollJobLogBottom="scrollBottom"
diff --git a/app/assets/javascripts/jobs/components/job_log_controllers.vue b/app/assets/javascripts/jobs/components/job_log_controllers.vue
index 5e89dd5acc2..9f5dbe70331 100644
--- a/app/assets/javascripts/jobs/components/job_log_controllers.vue
+++ b/app/assets/javascripts/jobs/components/job_log_controllers.vue
@@ -1,6 +1,6 @@
<script>
import { GlTooltipDirective, GlLink, GlButton, GlSearchBoxByClick } from '@gitlab/ui';
-import { scrollToElement } from '~/lib/utils/common_utils';
+import { scrollToElement, backOff } from '~/lib/utils/common_utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { __, s__, sprintf } from '~/locale';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
@@ -10,6 +10,7 @@ export default {
i18n: {
scrollToBottomButtonLabel: s__('Job|Scroll to bottom'),
scrollToTopButtonLabel: s__('Job|Scroll to top'),
+ scrollToNextFailureButtonLabel: s__('Job|Scroll to next failure'),
showRawButtonLabel: s__('Job|Show complete raw'),
searchPlaceholder: s__('Job|Search job log'),
noResults: s__('Job|No search results found'),
@@ -55,6 +56,10 @@ export default {
type: Boolean,
required: true,
},
+ isComplete: {
+ type: Boolean,
+ required: true,
+ },
jobLog: {
type: Array,
required: true,
@@ -64,6 +69,8 @@ export default {
return {
searchTerm: '',
searchResults: [],
+ failureCount: null,
+ failureIndex: 0,
};
},
computed: {
@@ -75,13 +82,49 @@ export default {
showJobLogSearch() {
return this.glFeatures.jobLogSearch;
},
+ showJumpToFailures() {
+ return this.glFeatures.jobLogJumpToFailures;
+ },
+ hasFailures() {
+ return this.failureCount > 0;
+ },
+ shouldDisableJumpToFailures() {
+ return !this.hasFailures;
+ },
+ },
+ mounted() {
+ this.checkFailureCount();
},
methods: {
+ checkFailureCount() {
+ if (this.glFeatures.jobLogJumpToFailures) {
+ backOff((next, stop) => {
+ this.failureCount = document.querySelectorAll('.term-fg-l-red').length;
+
+ if (this.hasFailures || (this.isComplete && !this.hasFailures)) {
+ stop();
+ } else {
+ next();
+ }
+ });
+ }
+ },
+ handleScrollToNextFailure() {
+ const failures = document.querySelectorAll('.term-fg-l-red');
+ const nextFailure = failures[this.failureIndex];
+
+ if (nextFailure) {
+ nextFailure.scrollIntoView({ block: 'center' });
+ this.failureIndex = (this.failureIndex + 1) % failures.length;
+ }
+ },
handleScrollToTop() {
this.$emit('scrollJobLogTop');
+ this.failureIndex = 0;
},
handleScrollToBottom() {
this.$emit('scrollJobLogBottom');
+ this.failureIndex = 0;
},
searchJobLog() {
this.searchResults = [];
@@ -135,10 +178,10 @@ export default {
};
</script>
<template>
- <div class="top-bar">
+ <div class="top-bar gl-display-flex gl-justify-content-space-between">
<!-- truncate information -->
<div
- class="truncated-info gl-display-none gl-sm-display-block gl-float-left"
+ class="truncated-info gl-display-none gl-sm-display-flex gl-flex-wrap gl-align-items-center"
data-testid="log-truncated-info"
>
<template v-if="isJobLogSizeVisible">
@@ -154,7 +197,7 @@ export default {
</div>
<!-- eo truncate information -->
- <div class="controllers gl-float-right">
+ <div class="controllers">
<template v-if="showJobLogSearch">
<gl-search-box-by-click
v-model="searchTerm"
@@ -187,6 +230,18 @@ export default {
<!-- eo links -->
<!-- scroll buttons -->
+ <gl-button
+ v-if="showJumpToFailures"
+ v-gl-tooltip
+ :title="$options.i18n.scrollToNextFailureButtonLabel"
+ :aria-label="$options.i18n.scrollToNextFailureButtonLabel"
+ :disabled="shouldDisableJumpToFailures"
+ class="btn-scroll gl-ml-3"
+ data-testid="job-controller-scroll-to-failure"
+ icon="soft-wrap"
+ @click="handleScrollToNextFailure"
+ />
+
<div v-gl-tooltip :title="$options.i18n.scrollToTopButtonLabel" class="gl-ml-3">
<gl-button
:disabled="isScrollTopDisabled"
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 031f5dc45ca..b62d12a3971 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -427,10 +427,10 @@
padding-inline-start: 28px;
margin-inline-start: 0 !important;
- > input.task-list-item-checkbox {
+ input.task-list-item-checkbox {
position: absolute;
- inset-inline-start: 8px;
- top: 5px;
+ inset-inline-start: $gl-padding-8;
+ inset-block-start: 5px;
}
}
}
diff --git a/app/assets/stylesheets/page_bundles/escalation_policies.scss b/app/assets/stylesheets/page_bundles/escalation_policies.scss
index 6f3873fea0c..84c62ba93dd 100644
--- a/app/assets/stylesheets/page_bundles/escalation_policies.scss
+++ b/app/assets/stylesheets/page_bundles/escalation_policies.scss
@@ -15,17 +15,11 @@
$stroke-size: 1px;
.right-arrow {
- @include gl-relative;
height: $stroke-size;
- background-color: var(--gray-900, $gray-900);
- min-width: $gl-spacing-scale-7;
&-head {
- @include gl-absolute;
- top: -2*$stroke-size;
- left: calc(100% - #{5*$stroke-size});
- @include gl-p-1;
- @include gl-border-solid;
+ top: -2 * $stroke-size;
+ left: calc(100% - #{5 * $stroke-size});
border-width: 0 $stroke-size $stroke-size 0;
border-color: var(--gray-900, $gray-900);
transform: rotate(-45deg);
@@ -41,14 +35,10 @@ $stroke-size: 1px;
.rule-condition {
@media (min-width: $breakpoint-lg) {
flex-basis: 25%;
- flex-shrink: 0;
+ @include gl-flex-shrink-0;
}
@media (max-width: $breakpoint-lg) {
@include gl-w-full;
}
}
-
-.rule-action {
- min-width: 0;
-}
diff --git a/app/assets/stylesheets/page_bundles/issues_show.scss b/app/assets/stylesheets/page_bundles/issues_show.scss
index c664e0a734e..9876003e652 100644
--- a/app/assets/stylesheets/page_bundles/issues_show.scss
+++ b/app/assets/stylesheets/page_bundles/issues_show.scss
@@ -1,77 +1,24 @@
@import 'mixins_and_variables_and_functions';
.description {
- ul,
- ol {
- /* We're changing list-style-position to inside because the default of
- * outside doesn't move negative margin to the left of the bullet. */
- list-style-position: inside;
+ ul.task-list > li.task-list-item {
+ margin-inline-start: 0.5rem !important; /* Override typography.scss */
}
li {
position: relative;
- /* In the browser, the li element comes after (to the right of) the bullet point, so hovering
- * over the left of the bullet point doesn't trigger a row hover. To trigger hovering on the
- * left, we're applying negative margin here to shift the li element left. */
- margin-inline-start: -1rem;
- padding-inline-start: 2.5rem;
+ margin-inline-start: 2.25rem;
+
+ &.task-list-item > .drag-icon {
+ inset-inline-start: -0.6rem;
+ }
.drag-icon {
position: absolute;
inset-block-start: 0.3rem;
- inset-inline-start: 1rem;
- }
-
- /* The inside bullet aligns itself to the bottom, which we see when text to the right of
- * a multi-line list item wraps. We fix this by aligning it to the top, and excluding
- * other elements. Targeting ::marker doesn't seem to work, instead we exclude custom elements
- * or anything with a class */
- > *:not(gl-emoji, code, [class]) {
- vertical-align: top;
- }
-
- /* The inside bullet is treated like an element inside the li element, so when we have a
- * multi-paragraph list item, the text doesn't start on the right of the bullet because
- * it is a block level p element. We make it inline to fix this. */
- > p:first-of-type {
- display: inline-block;
- max-width: calc(100% - 1.5rem);
- }
-
- /* We fix the other paragraphs not indenting to the
- * right of the bullet due to the inside bullet. */
- p ~ a,
- p ~ blockquote,
- p ~ code,
- p ~ details,
- p ~ dl,
- p ~ h1,
- p ~ h2,
- p ~ h3,
- p ~ h4,
- p ~ h5,
- p ~ h6,
- p ~ hr,
- p ~ ol,
- p ~ p,
- p ~ table:not(.code), /* We need :not(.code) to override typography.scss */
- p ~ ul,
- p ~ .markdown-code-block {
- margin-inline-start: 1rem;
- }
- }
-
- ul.task-list {
- > li.task-list-item {
- /* We're using !important to override the same selector in typography.scss */
- margin-inline-start: -1rem !important;
- padding-inline-start: 2.5rem;
-
- > input.task-list-item-checkbox {
- position: static;
- vertical-align: middle;
- margin-block-start: -2px;
- }
+ inset-inline-start: -2.3rem;
+ padding-inline-end: 1rem;
+ width: 2rem;
}
}
}
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index ad59f421c06..8570bbf1dbd 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -19,6 +19,7 @@ class Projects::JobsController < Projects::ApplicationController
before_action :authorize_create_proxy_build!, only: :proxy_websocket_authorize
before_action :verify_proxy_request!, only: :proxy_websocket_authorize
before_action :push_job_log_search, only: [:show]
+ before_action :push_job_log_jump_to_failures, only: [:show]
before_action :reject_if_build_artifacts_size_refreshing!, only: [:erase]
layout 'project'
@@ -252,4 +253,8 @@ class Projects::JobsController < Projects::ApplicationController
def push_job_log_search
push_frontend_feature_flag(:job_log_search, @project)
end
+
+ def push_job_log_jump_to_failures
+ push_frontend_feature_flag(:job_log_jump_to_failures, @project)
+ end
end
diff --git a/config/feature_flags/development/job_log_jump_to_failures.yml b/config/feature_flags/development/job_log_jump_to_failures.yml
new file mode 100644
index 00000000000..45d11c82fe9
--- /dev/null
+++ b/config/feature_flags/development/job_log_jump_to_failures.yml
@@ -0,0 +1,8 @@
+---
+name: job_log_jump_to_failures
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/91098
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/368633
+milestone: '15.3'
+type: development
+group: group::pipeline execution
+default_enabled: false
diff --git a/doc/api/group_protected_environments.md b/doc/api/group_protected_environments.md
index 8ebd0dcd99a..0f1527f8968 100644
--- a/doc/api/group_protected_environments.md
+++ b/doc/api/group_protected_environments.md
@@ -106,7 +106,7 @@ POST /groups/:id/protected_environments
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](index.md#namespaced-path-encoding) maintained by the authenticated user. |
| `name` | string | yes | The deployment tier of the protected environment. One of `production`, `staging`, `testing`, `development`, or `other`. Read more about [deployment tiers](../ci/environments/index.md#deployment-tier-of-environments).|
| `deploy_access_levels` | array | yes | Array of access levels allowed to deploy, with each described by a hash. One of `user_id`, `group_id` or `access_level`. They take the form of `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}` respectively. |
-| `required_approval_count` | integer | no | The number of approvals required to deploy to this environment. This is part of Deployment Approvals, which isn't yet available for use. For details, see [issue](https://gitlab.com/gitlab-org/gitlab/-/issues/343864). |
+| `required_approval_count` | integer | no | The number of approvals required to deploy to this environment. |
| `approval_rules` | array | no | Array of access levels allowed to approve, with each described by a hash. One of `user_id`, `group_id` or `access_level`. They take the form of `{user_id: integer}`, `{group_id: integer}` or `{access_level: integer}` respectively. You can also specify the number of required approvals from the specified entity with `required_approvals` field. See [Multiple approval rules](../ci/environments/deployment_approvals.md#multiple-approval-rules) for more information. |
The assignable `user_id` are the users who belong to the given group with the Maintainer role (or above).
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index 6dd9f6e72c5..0c517d07f41 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -709,7 +709,6 @@ args: {
security: {
authn_requests_signed: true, # enable signature on AuthNRequest
want_assertions_signed: true, # enable the requirement of signed assertion
- embed_sign: true, # embedded signature or HTTP GET parameter signature
metadata_signed: false, # enable signature on Metadata
signature_method: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
digest_method: 'http://www.w3.org/2001/04/xmlenc#sha256',
diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md
index 532698d28e3..c4672e61c99 100644
--- a/doc/user/application_security/dependency_scanning/index.md
+++ b/doc/user/application_security/dependency_scanning/index.md
@@ -601,7 +601,7 @@ The following variables allow configuration of global dependency scanning settin
| `ADDITIONAL_CA_CERT_BUNDLE` | Bundle of CA certs to trust. The bundle of certificates provided here is also used by other tools during the scanning process, such as `git`, `yarn`, or `npm`. See [Using a custom SSL CA certificate authority](#using-a-custom-ssl-ca-certificate-authority) for more details. |
| `DS_EXCLUDED_ANALYZERS` | Specify the analyzers (by name) to exclude from Dependency Scanning. For more information, see [Dependency Scanning Analyzers](analyzers.md). |
| `DS_DEFAULT_ANALYZERS` | This feature was [deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/287691) in GitLab 14.0 and [removed](https://gitlab.com/gitlab-org/gitlab/-/issues/333299) in 15.0. Use `DS_EXCLUDED_ANALYZERS` instead. |
-| `DS_EXCLUDED_PATHS` | Exclude files and directories from the scan based on the paths. A comma-separated list of patterns. Patterns can be globs, or file or folder paths (for example, `doc,spec`). Parent directories also match patterns. Default: `"spec, test, tests, tmp"`. |
+| `DS_EXCLUDED_PATHS` | Exclude files and directories from the scan based on the paths. A comma-separated list of patterns. Patterns can be globs (see [`doublestar.Match`](https://pkg.go.dev/github.com/bmatcuk/doublestar/v4@v4.0.2#Match) for supported patterns), or file or folder paths (for example, `doc,spec`). Parent directories also match patterns. Default: `"spec, test, tests, tmp"`. |
| `DS_IMAGE_SUFFIX` | Suffix added to the image name. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/354796) in GitLab 14.10.) Automatically set to `"-fips"` when FIPS mode is enabled. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/357922) in GitLab 15.0.) |
| `SECURE_ANALYZERS_PREFIX` | Override the name of the Docker registry providing the official default images (proxy). Read more about [customizing analyzers](analyzers.md). |
| `SECURE_LOG_LEVEL` | Set the minimum logging level. Messages of this logging level or higher are output. From highest to lowest severity, the logging levels are: `fatal`, `error`, `warn`, `info`, `debug`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10880) in GitLab 13.1. Default: `info`. |
diff --git a/doc/user/application_security/get-started-security.md b/doc/user/application_security/get-started-security.md
new file mode 100644
index 00000000000..4c2b971b5fa
--- /dev/null
+++ b/doc/user/application_security/get-started-security.md
@@ -0,0 +1,34 @@
+---
+stage: DevSecOps
+group: Technical writing
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# Get started with GitLab application security **(ULTIMATE)**
+
+Complete the following steps to get the most from GitLab application security tools.
+
+1. Enable [Secret Detection](secret_detection/index.md) scanning for your default branch.
+1. Enable [Dependency Scanning](dependency_scanning/index.md) for your default branch so you can start identifying existing
+ vulnerable packages in your codebase.
+1. Add security scans to feature branch pipelines. The same scans should be enabled as are running
+ on your default branch. Subsequent scans will show only new vulnerabilities by comparing the feature branch to the default branch results.
+1. Let your team get comfortable with [vulnerability reports](vulnerability_report/index.md) and
+ establish a vulnerability triage workflow.
+1. Consider creating [labels](../project/labels.md) and [issue boards](../project/issue_board.md) to
+ help manage issues created from vulnerabilities. Issue boards allow all stakeholders to have a
+ common view of all issues.
+1. Create a [scan result policy](policies/index.md) to limit new vulnerabilities from being merged
+ into your default branch.
+1. Monitor the [Security Dashboard](security_dashboard/index.md) trends to gauge success in
+ remediating existing vulnerabilities and preventing the introduction of new ones.
+1. Enable other scan types such as [SAST](sast/index.md), [DAST](dast/index.md),
+ [Fuzz testing](coverage_fuzzing/index.md), or [Container Scanning](container_scanning/index.md).
+ Be sure to add the same scan types to both feature pipelines and default branch pipelines.
+1. Use [Compliance Pipelines](../../user/project/settings/index.md#compliance-pipeline-configuration)
+ or [Scan Execution Policies](policies/scan-execution-policies.md) to enforce required scan types
+ and ensure separation of duties between security and engineering.
+1. Consider enabling [Review Apps](../../development/testing_guide/review_apps.md) to allow for DAST
+ and [Web API fuzzing](api_fuzzing/index.md) on ephemeral test environments.
+1. Enable [operational container scanning](../../user/clusters/agent/vulnerabilities.md) to scan
+ container images in your production cluster for security vulnerabilities.
diff --git a/lib/tasks/contracts/merge_requests.rake b/lib/tasks/contracts/merge_requests.rake
index 2fdd2beb4ee..fdcea804640 100644
--- a/lib/tasks/contracts/merge_requests.rake
+++ b/lib/tasks/contracts/merge_requests.rake
@@ -4,31 +4,30 @@ return if Rails.env.production?
require 'pact/tasks/verification_task'
-contracts = File.expand_path('../../../spec/contracts', __dir__)
-provider = File.expand_path('provider', contracts)
+contracts = File.expand_path('../../../spec/contracts/contracts/project/merge_request', __dir__)
+provider = File.expand_path('../../../spec/contracts/provider', __dir__)
# rubocop:disable Rails/RakeEnvironment
namespace :contracts do
namespace :merge_requests do
Pact::VerificationTask.new(:diffs_batch) do |pact|
pact.uri(
- "#{contracts}/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_batch_endpoint.json",
- pact_helper: "#{provider}/pact_helpers/project/merge_request/diffs_batch_helper.rb"
+ "#{contracts}/show/mergerequest#show-merge_request_diffs_batch_endpoint.json",
+ pact_helper: "#{provider}/pact_helpers/project/merge_request/show/diffs_batch_helper.rb"
)
end
Pact::VerificationTask.new(:diffs_metadata) do |pact|
pact.uri(
- "#{contracts}/contracts/project/merge_request/show/" \
- "mergerequest#show-merge_request_diffs_metadata_endpoint.json",
- pact_helper: "#{provider}/pact_helpers/project/merge_request/diffs_metadata_helper.rb"
+ "#{contracts}/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json",
+ pact_helper: "#{provider}/pact_helpers/project/merge_request/show/diffs_metadata_helper.rb"
)
end
Pact::VerificationTask.new(:discussions) do |pact|
pact.uri(
- "#{contracts}/contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json",
- pact_helper: "#{provider}/pact_helpers/project/merge_request/discussions_helper.rb"
+ "#{contracts}/show/mergerequest#show-merge_request_discussions_endpoint.json",
+ pact_helper: "#{provider}/pact_helpers/project/merge_request/show/discussions_helper.rb"
)
end
diff --git a/lib/tasks/contracts/pipeline_schedules.rake b/lib/tasks/contracts/pipeline_schedules.rake
index b6d331448e2..136577730c8 100644
--- a/lib/tasks/contracts/pipeline_schedules.rake
+++ b/lib/tasks/contracts/pipeline_schedules.rake
@@ -5,7 +5,7 @@ return if Rails.env.production?
require 'pact/tasks/verification_task'
contracts = File.expand_path('../../../spec/contracts/contracts/project/pipeline_schedule', __dir__)
-provider = File.expand_path('../../../provider', contracts)
+provider = File.expand_path('../../../spec/contracts/provider', __dir__)
# rubocop:disable Rails/RakeEnvironment
namespace :contracts do
diff --git a/lib/tasks/contracts/pipelines.rake b/lib/tasks/contracts/pipelines.rake
index 75955822242..75d381e1f8d 100644
--- a/lib/tasks/contracts/pipelines.rake
+++ b/lib/tasks/contracts/pipelines.rake
@@ -4,36 +4,36 @@ return if Rails.env.production?
require 'pact/tasks/verification_task'
-contracts = File.expand_path('../../../spec/contracts', __dir__)
-provider = File.expand_path('provider', contracts)
+contracts = File.expand_path('../../../spec/contracts/contracts/project/pipeline', __dir__)
+provider = File.expand_path('../../../spec/contracts/provider', __dir__)
# rubocop:disable Rails/RakeEnvironment
namespace :contracts do
namespace :pipelines do
Pact::VerificationTask.new(:create_a_new_pipeline) do |pact|
pact.uri(
- "#{contracts}/contracts/project/pipeline/new/pipelines#new-post_create_a_new_pipeline.json",
- pact_helper: "#{provider}/pact_helpers/project/pipeline/create_a_new_pipeline_helper.rb"
+ "#{contracts}/new/pipelines#new-post_create_a_new_pipeline.json",
+ pact_helper: "#{provider}/pact_helpers/project/pipeline/index/create_a_new_pipeline_helper.rb"
)
end
Pact::VerificationTask.new(:get_list_project_pipelines) do |pact|
pact.uri(
- "#{contracts}/contracts/project/pipeline/index/pipelines#index-get_list_project_pipelines.json",
- pact_helper: "#{provider}/pact_helpers/project/pipeline/get_list_project_pipelines_helper.rb"
+ "#{contracts}/index/pipelines#index-get_list_project_pipelines.json",
+ pact_helper: "#{provider}/pact_helpers/project/pipeline/index/get_list_project_pipelines_helper.rb"
)
end
Pact::VerificationTask.new(:get_pipeline_header_data) do |pact|
pact.uri(
- "#{contracts}/contracts/project/pipeline/show/pipelines#show-get_pipeline_header_data.json",
+ "#{contracts}/show/pipelines#show-get_pipeline_header_data.json",
pact_helper: "#{provider}/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb"
)
end
Pact::VerificationTask.new(:delete_pipeline) do |pact|
pact.uri(
- "#{contracts}/contracts/project/pipeline/show/pipelines#show-delete_pipeline.json",
+ "#{contracts}/show/pipelines#show-delete_pipeline.json",
pact_helper: "#{provider}/pact_helpers/project/pipeline/show/delete_pipeline_helper.rb"
)
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index cc522a763b2..923a24a4dfd 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -22656,6 +22656,9 @@ msgstr ""
msgid "Job|Scroll to bottom"
msgstr ""
+msgid "Job|Scroll to next failure"
+msgstr ""
+
msgid "Job|Scroll to top"
msgstr ""
diff --git a/spec/contracts/consumer/endpoints/project/pipelines.js b/spec/contracts/consumer/endpoints/project/pipelines.js
deleted file mode 100644
index 33758dee75b..00000000000
--- a/spec/contracts/consumer/endpoints/project/pipelines.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import { request } from 'axios';
-
-export function getProjectPipelines(endpoint) {
- const { url } = endpoint;
-
- return request({
- method: 'GET',
- baseURL: url,
- url: '/gitlab-org/gitlab-qa/-/pipelines.json',
- headers: { Accept: '*/*' },
- params: {
- scope: 'all',
- page: 1,
- },
- }).then((response) => response.data);
-}
diff --git a/spec/contracts/consumer/fixtures/project/merge_request/diffs_batch.fixture.js b/spec/contracts/consumer/fixtures/project/merge_request/diffs_batch.fixture.js
index b53e4bb335d..673aad721b3 100644
--- a/spec/contracts/consumer/fixtures/project/merge_request/diffs_batch.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/merge_request/diffs_batch.fixture.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { Matchers } from '@pact-foundation/pact';
const body = {
@@ -73,8 +71,12 @@ const DiffsBatch = {
body,
},
- request: {
+ scenario: {
+ state: 'a merge request with diffs exists',
uponReceiving: 'a request for diff lines',
+ },
+
+ request: {
withRequest: {
method: 'GET',
path: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json',
@@ -87,5 +89,3 @@ const DiffsBatch = {
};
export { DiffsBatch };
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/fixtures/project/merge_request/diffs_metadata.fixture.js b/spec/contracts/consumer/fixtures/project/merge_request/diffs_metadata.fixture.js
index 39dbcf78ee7..2fee4a02023 100644
--- a/spec/contracts/consumer/fixtures/project/merge_request/diffs_metadata.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/merge_request/diffs_metadata.fixture.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { Matchers } from '@pact-foundation/pact';
const body = {
@@ -81,8 +79,12 @@ const DiffsMetadata = {
body,
},
+ scenario: {
+ state: 'a merge request exists',
+ uponReceiving: 'a request for diffs metadata',
+ },
+
request: {
- uponReceiving: 'a request for Diffs Metadata',
withRequest: {
method: 'GET',
path: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json',
@@ -94,5 +96,3 @@ const DiffsMetadata = {
};
export { DiffsMetadata };
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/fixtures/project/merge_request/discussions.fixture.js b/spec/contracts/consumer/fixtures/project/merge_request/discussions.fixture.js
index af0962a01cb..8c392395e1c 100644
--- a/spec/contracts/consumer/fixtures/project/merge_request/discussions.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/merge_request/discussions.fixture.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { Matchers } from '@pact-foundation/pact';
const body = Matchers.eachLike({
@@ -70,8 +68,12 @@ const Discussions = {
body,
},
- request: {
+ scenario: {
+ state: 'a merge request with discussions exists',
uponReceiving: 'a request for discussions',
+ },
+
+ request: {
withRequest: {
method: 'GET',
path: '/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json',
@@ -83,5 +85,3 @@ const Discussions = {
};
export { Discussions };
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/fixtures/project/pipeline/create_a_new_pipeline.fixture.js b/spec/contracts/consumer/fixtures/project/pipeline/create_a_new_pipeline.fixture.js
index a732189e6fb..68063d2fb0c 100644
--- a/spec/contracts/consumer/fixtures/project/pipeline/create_a_new_pipeline.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/pipeline/create_a_new_pipeline.fixture.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { Matchers } from '@pact-foundation/pact';
import { REDIRECT_HTML } from '../../../helpers/common_regex_patterns';
@@ -39,5 +37,3 @@ const NewProjectPipeline = {
};
export { NewProjectPipeline };
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/fixtures/project/pipeline/delete_pipeline.fixture.js b/spec/contracts/consumer/fixtures/project/pipeline/delete_pipeline.fixture.js
index 4dbb694064b..2e3e7355b99 100644
--- a/spec/contracts/consumer/fixtures/project/pipeline/delete_pipeline.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/pipeline/delete_pipeline.fixture.js
@@ -6,6 +6,11 @@ const DeletePipeline = {
},
},
+ scenario: {
+ state: 'a pipeline for a project exists',
+ uponReceiving: 'a request to delete the pipeline',
+ },
+
request: {
method: 'POST',
path: '/api/graphql',
diff --git a/spec/contracts/consumer/fixtures/project/pipeline/get_list_project_pipelines.fixture.js b/spec/contracts/consumer/fixtures/project/pipeline/get_list_project_pipelines.fixture.js
index 8a7663325b9..a982e927572 100644
--- a/spec/contracts/consumer/fixtures/project/pipeline/get_list_project_pipelines.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/pipeline/get_list_project_pipelines.fixture.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { Matchers } from '@pact-foundation/pact';
import {
URL,
@@ -225,8 +223,12 @@ const ProjectPipelines = {
body,
},
- request: {
+ scenario: {
+ state: 'a few pipelines for a project exists',
uponReceiving: 'a request for a list of project pipelines',
+ },
+
+ request: {
withRequest: {
method: 'GET',
path: '/gitlab-org/gitlab-qa/-/pipelines.json',
@@ -239,5 +241,3 @@ const ProjectPipelines = {
};
export { ProjectPipelines };
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/fixtures/project/pipeline/get_pipeline_header_data.fixture.js b/spec/contracts/consumer/fixtures/project/pipeline/get_pipeline_header_data.fixture.js
index f51ed9c2c74..b14a230d2e0 100644
--- a/spec/contracts/consumer/fixtures/project/pipeline/get_pipeline_header_data.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/pipeline/get_pipeline_header_data.fixture.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { Matchers } from '@pact-foundation/pact';
import {
JOB_STATUSES,
@@ -83,6 +81,11 @@ const PipelineHeaderData = {
body,
},
+ scenario: {
+ state: 'a pipeline for a project exists',
+ uponReceiving: 'a request for the pipeline header data',
+ },
+
request: {
method: 'POST',
path: '/api/graphql',
@@ -95,5 +98,3 @@ const PipelineHeaderData = {
};
export { PipelineHeaderData };
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture.js b/spec/contracts/consumer/fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture.js
index acdc94d5c6e..acfab14851a 100644
--- a/spec/contracts/consumer/fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture.js
+++ b/spec/contracts/consumer/fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { Matchers } from '@pact-foundation/pact';
import { REDIRECT_HTML } from '../../../helpers/common_regex_patterns';
@@ -44,5 +42,3 @@ const UpdatePipelineSchedule = {
};
export { UpdatePipelineSchedule };
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/endpoints/project/merge_requests.js b/spec/contracts/consumer/resources/api/project/merge_requests.js
index 38773e5fb10..e52743cede2 100644
--- a/spec/contracts/consumer/endpoints/project/merge_requests.js
+++ b/spec/contracts/consumer/resources/api/project/merge_requests.js
@@ -1,9 +1,9 @@
-import { request } from 'axios';
+import axios from 'axios';
-export function getDiffsMetadata(endpoint) {
+export async function getDiffsMetadata(endpoint) {
const { url } = endpoint;
- return request({
+ return axios({
method: 'GET',
baseURL: url,
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json',
@@ -11,10 +11,10 @@ export function getDiffsMetadata(endpoint) {
}).then((response) => response.data);
}
-export function getDiscussions(endpoint) {
+export async function getDiscussions(endpoint) {
const { url } = endpoint;
- return request({
+ return axios({
method: 'GET',
baseURL: url,
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json',
@@ -22,10 +22,10 @@ export function getDiscussions(endpoint) {
}).then((response) => response.data);
}
-export function getDiffsBatch(endpoint) {
+export async function getDiffsBatch(endpoint) {
const { url } = endpoint;
- return request({
+ return axios({
method: 'GET',
baseURL: url,
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json?page=0',
diff --git a/spec/contracts/consumer/resources/api/project/pipelines.js b/spec/contracts/consumer/resources/api/project/pipelines.js
index f58a7a5fb0f..8c6f5199666 100644
--- a/spec/contracts/consumer/resources/api/project/pipelines.js
+++ b/spec/contracts/consumer/resources/api/project/pipelines.js
@@ -1,5 +1,20 @@
import axios from 'axios';
+export async function getProjectPipelines(endpoint) {
+ const { url } = endpoint;
+
+ return axios({
+ method: 'GET',
+ baseURL: url,
+ url: '/gitlab-org/gitlab-qa/-/pipelines.json',
+ headers: { Accept: '*/*' },
+ params: {
+ scope: 'all',
+ page: 1,
+ },
+ }).then((response) => response.data);
+}
+
export async function postProjectPipelines(endpoint) {
const { url } = endpoint;
diff --git a/spec/contracts/consumer/specs/project/merge_request/show.spec.js b/spec/contracts/consumer/specs/project/merge_request/show.spec.js
index 8c6e029cb12..4183e19435a 100644
--- a/spec/contracts/consumer/specs/project/merge_request/show.spec.js
+++ b/spec/contracts/consumer/specs/project/merge_request/show.spec.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { pactWith } from 'jest-pact';
import { DiffsBatch } from '../../../fixtures/project/merge_request/diffs_batch.fixture';
@@ -9,7 +7,7 @@ import {
getDiffsBatch,
getDiffsMetadata,
getDiscussions,
-} from '../../../endpoints/project/merge_requests';
+} from '../../../resources/api/project/merge_requests';
const CONSUMER_NAME = 'MergeRequest#show';
const CONSUMER_LOG = '../logs/consumer.log';
@@ -31,19 +29,19 @@ pactWith(
describe(DIFFS_BATCH_PROVIDER_NAME, () => {
beforeEach(() => {
const interaction = {
- state: 'a merge request with diffs exists',
+ ...DiffsBatch.scenario,
...DiffsBatch.request,
willRespondWith: DiffsBatch.success,
};
provider.addInteraction(interaction);
});
- it('returns a successful body', () => {
- return getDiffsBatch({
+ it('returns a successful body', async () => {
+ const diffsBatch = await getDiffsBatch({
url: provider.mockService.baseUrl,
- }).then((diffsBatch) => {
- expect(diffsBatch).toEqual(DiffsBatch.body);
});
+
+ expect(diffsBatch).toEqual(DiffsBatch.body);
});
});
},
@@ -61,19 +59,19 @@ pactWith(
describe(DISCUSSIONS_PROVIDER_NAME, () => {
beforeEach(() => {
const interaction = {
- state: 'a merge request with discussions exists',
+ ...Discussions.scenario,
...Discussions.request,
willRespondWith: Discussions.success,
};
provider.addInteraction(interaction);
});
- it('return a successful body', () => {
- return getDiscussions({
+ it('return a successful body', async () => {
+ const discussions = await getDiscussions({
url: provider.mockService.baseUrl,
- }).then((discussions) => {
- expect(discussions).toEqual(Discussions.body);
});
+
+ expect(discussions).toEqual(Discussions.body);
});
});
},
@@ -91,22 +89,20 @@ pactWith(
describe(DIFFS_METADATA_PROVIDER_NAME, () => {
beforeEach(() => {
const interaction = {
- state: 'a merge request exists',
+ ...DiffsMetadata.scenario,
...DiffsMetadata.request,
willRespondWith: DiffsMetadata.success,
};
provider.addInteraction(interaction);
});
- it('return a successful body', () => {
- return getDiffsMetadata({
+ it('return a successful body', async () => {
+ const diffsMetadata = await getDiffsMetadata({
url: provider.mockService.baseUrl,
- }).then((diffsMetadata) => {
- expect(diffsMetadata).toEqual(DiffsMetadata.body);
});
+
+ expect(diffsMetadata).toEqual(DiffsMetadata.body);
});
});
},
);
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/specs/project/pipeline/index.spec.js b/spec/contracts/consumer/specs/project/pipeline/index.spec.js
index 1c0358a3e28..1453435d637 100644
--- a/spec/contracts/consumer/specs/project/pipeline/index.spec.js
+++ b/spec/contracts/consumer/specs/project/pipeline/index.spec.js
@@ -1,9 +1,7 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { pactWith } from 'jest-pact';
import { ProjectPipelines } from '../../../fixtures/project/pipeline/get_list_project_pipelines.fixture';
-import { getProjectPipelines } from '../../../endpoints/project/pipelines';
+import { getProjectPipelines } from '../../../resources/api/project/pipelines';
const CONSUMER_NAME = 'Pipelines#index';
const CONSUMER_LOG = '../logs/consumer.log';
@@ -23,22 +21,20 @@ pactWith(
describe(PROVIDER_NAME, () => {
beforeEach(() => {
const interaction = {
- state: 'a few pipelines for a project exists',
+ ...ProjectPipelines.scenario,
...ProjectPipelines.request,
willRespondWith: ProjectPipelines.success,
};
provider.addInteraction(interaction);
});
- it('returns a successful body', () => {
- return getProjectPipelines({
+ it('returns a successful body', async () => {
+ const pipelines = await getProjectPipelines({
url: provider.mockService.baseUrl,
- }).then((pipelines) => {
- expect(pipelines).toEqual(ProjectPipelines.body);
});
+
+ expect(pipelines).toEqual(ProjectPipelines.body);
});
});
},
);
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/specs/project/pipeline/new.spec.js b/spec/contracts/consumer/specs/project/pipeline/new.spec.js
index 621b31af52d..c3824d5979e 100644
--- a/spec/contracts/consumer/specs/project/pipeline/new.spec.js
+++ b/spec/contracts/consumer/specs/project/pipeline/new.spec.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { pactWith } from 'jest-pact';
import { NewProjectPipeline } from '../../../fixtures/project/pipeline/create_a_new_pipeline.fixture';
@@ -41,5 +39,3 @@ pactWith(
});
},
);
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/specs/project/pipeline/show.spec.js b/spec/contracts/consumer/specs/project/pipeline/show.spec.js
index 2c5ba964e2e..be6abb78eb5 100644
--- a/spec/contracts/consumer/specs/project/pipeline/show.spec.js
+++ b/spec/contracts/consumer/specs/project/pipeline/show.spec.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { pactWith } from 'jest-pact';
import { GraphQLInteraction } from '@pact-foundation/pact';
@@ -32,8 +30,8 @@ pactWith(
'app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql',
);
const graphqlQuery = new GraphQLInteraction()
- .given('a pipeline for a project exists')
- .uponReceiving('a request for the pipeline header data')
+ .given(PipelineHeaderData.scenario.state)
+ .uponReceiving(PipelineHeaderData.scenario.uponReceiving)
.withQuery(query)
.withRequest(PipelineHeaderData.request)
.withVariables(PipelineHeaderData.variables)
@@ -69,8 +67,8 @@ pactWith(
'app/assets/javascripts/pipelines/graphql/mutations/delete_pipeline.mutation.graphql',
);
const graphqlQuery = new GraphQLInteraction()
- .given('a pipeline for a project exists')
- .uponReceiving('a request to delete the pipeline')
+ .given(DeletePipeline.scenario.state)
+ .uponReceiving(DeletePipeline.scenario.uponReceiving)
.withQuery(query)
.withRequest(DeletePipeline.request)
.withVariables(DeletePipeline.variables)
@@ -89,5 +87,3 @@ pactWith(
});
},
);
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/consumer/specs/project/pipeline_schedule/edit.spec.js b/spec/contracts/consumer/specs/project/pipeline_schedule/edit.spec.js
index 7d89825bcd4..117e6754255 100644
--- a/spec/contracts/consumer/specs/project/pipeline_schedule/edit.spec.js
+++ b/spec/contracts/consumer/specs/project/pipeline_schedule/edit.spec.js
@@ -1,5 +1,3 @@
-/* eslint-disable @gitlab/require-i18n-strings */
-
import { pactWith } from 'jest-pact';
import { UpdatePipelineSchedule } from '../../../fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture';
@@ -41,5 +39,3 @@ pactWith(
});
},
);
-
-/* eslint-enable @gitlab/require-i18n-strings */
diff --git a/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json b/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json
index b98a0127e54..c59a3d55f43 100644
--- a/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json
+++ b/spec/contracts/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json
@@ -7,7 +7,7 @@
},
"interactions": [
{
- "description": "a request for Diffs Metadata",
+ "description": "a request for diffs metadata",
"providerState": "a merge request exists",
"request": {
"method": "GET",
@@ -220,4 +220,4 @@
"version": "2.0.0"
}
}
-} \ No newline at end of file
+}
diff --git a/spec/contracts/provider/pact_helpers/project/merge_request/diffs_batch_helper.rb b/spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_batch_helper.rb
index 7d1fbe91e86..f94ce47b1f3 100644
--- a/spec/contracts/provider/pact_helpers/project/merge_request/diffs_batch_helper.rb
+++ b/spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_batch_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative '../../../spec_helper'
-require_relative '../../../states/project/merge_request/diffs_batch_state'
+require_relative '../../../../spec_helper'
+require_relative '../../../../states/project/merge_request/show_state'
module Provider
module DiffsBatchHelper
diff --git a/spec/contracts/provider/pact_helpers/project/merge_request/diffs_metadata_helper.rb b/spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_metadata_helper.rb
index 5f0c58d18d4..61567214b7a 100644
--- a/spec/contracts/provider/pact_helpers/project/merge_request/diffs_metadata_helper.rb
+++ b/spec/contracts/provider/pact_helpers/project/merge_request/show/diffs_metadata_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative '../../../spec_helper'
-require_relative '../../../states/project/merge_request/diffs_metadata_state'
+require_relative '../../../../spec_helper'
+require_relative '../../../../states/project/merge_request/show_state'
module Provider
module DiffsMetadataHelper
diff --git a/spec/contracts/provider/pact_helpers/project/merge_request/discussions_helper.rb b/spec/contracts/provider/pact_helpers/project/merge_request/show/discussions_helper.rb
index 0f4244ba40a..fa76ce8889a 100644
--- a/spec/contracts/provider/pact_helpers/project/merge_request/discussions_helper.rb
+++ b/spec/contracts/provider/pact_helpers/project/merge_request/show/discussions_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative '../../../spec_helper'
-require_relative '../../../states/project/merge_request/discussions_state'
+require_relative '../../../../spec_helper'
+require_relative '../../../../states/project/merge_request/show_state'
module Provider
module DiscussionsHelper
diff --git a/spec/contracts/provider/pact_helpers/project/pipeline/create_a_new_pipeline_helper.rb b/spec/contracts/provider/pact_helpers/project/pipeline/index/create_a_new_pipeline_helper.rb
index e7d9b1452d3..247a7c4ca8e 100644
--- a/spec/contracts/provider/pact_helpers/project/pipeline/create_a_new_pipeline_helper.rb
+++ b/spec/contracts/provider/pact_helpers/project/pipeline/index/create_a_new_pipeline_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative '../../../spec_helper'
-require_relative '../../../states/project/pipeline/new_state'
+require_relative '../../../../spec_helper'
+require_relative '../../../../states/project/pipeline/new_state'
module Provider
module CreateNewPipelineHelper
diff --git a/spec/contracts/provider/pact_helpers/project/pipeline/get_list_project_pipelines_helper.rb b/spec/contracts/provider/pact_helpers/project/pipeline/index/get_list_project_pipelines_helper.rb
index 5307468b7c6..80cbbe3b4dd 100644
--- a/spec/contracts/provider/pact_helpers/project/pipeline/get_list_project_pipelines_helper.rb
+++ b/spec/contracts/provider/pact_helpers/project/pipeline/index/get_list_project_pipelines_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-require_relative '../../../spec_helper'
-require_relative '../../../states/project/pipeline/pipelines_state'
+require_relative '../../../../spec_helper'
+require_relative '../../../../states/project/pipeline/index_state'
module Provider
module GetListProjectPipelinesHelper
diff --git a/spec/contracts/provider/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb b/spec/contracts/provider/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb
index 4bc7dff2ef9..bc8c04cc455 100644
--- a/spec/contracts/provider/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb
+++ b/spec/contracts/provider/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require_relative '../../../../spec_helper'
-require_relative '../../../../states/project/pipeline/pipeline_state'
+require_relative '../../../../states/project/pipeline/show_state'
module Provider
module GetPipelinesHeaderDataHelper
diff --git a/spec/contracts/provider/states/project/merge_request/diffs_batch_state.rb b/spec/contracts/provider/states/project/merge_request/diffs_batch_state.rb
deleted file mode 100644
index ac20c17c187..00000000000
--- a/spec/contracts/provider/states/project/merge_request/diffs_batch_state.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-Pact.provider_states_for "MergeRequest#show" do
- provider_state "a merge request with diffs exists" do
- set_up do
- user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
- namespace = create(:namespace, name: 'gitlab-org')
- project = create(:project, :custom_repo, name: 'gitlab-qa', namespace: namespace, files: {})
-
- project.add_maintainer(user)
-
- merge_request = create(:merge_request_with_multiple_diffs, source_project: project)
- merge_request_diff = create(:merge_request_diff, merge_request: merge_request)
-
- create(:merge_request_diff_file, :new_file, merge_request_diff: merge_request_diff)
- end
- end
-end
diff --git a/spec/contracts/provider/states/project/merge_request/diffs_metadata_state.rb b/spec/contracts/provider/states/project/merge_request/diffs_metadata_state.rb
deleted file mode 100644
index 8754232690c..00000000000
--- a/spec/contracts/provider/states/project/merge_request/diffs_metadata_state.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# frozen_string_literal: true
-
-Pact.provider_states_for "MergeRequest#show" do
- provider_state "a merge request exists" do
- set_up do
- user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
- namespace = create(:namespace, name: 'gitlab-org')
- project = create(:project, :custom_repo, name: 'gitlab-qa', namespace: namespace, files: {})
-
- project.add_maintainer(user)
-
- merge_request = create(:merge_request, source_project: project)
- merge_request_diff = create(:merge_request_diff, merge_request: merge_request)
-
- create(:merge_request_diff_file, :new_file, merge_request_diff: merge_request_diff)
- end
- end
-end
diff --git a/spec/contracts/provider/states/project/merge_request/discussions_state.rb b/spec/contracts/provider/states/project/merge_request/discussions_state.rb
deleted file mode 100644
index 2d64f85eedf..00000000000
--- a/spec/contracts/provider/states/project/merge_request/discussions_state.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-Pact.provider_states_for "MergeRequest#show" do
- provider_state "a merge request with discussions exists" do
- set_up do
- user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
- namespace = create(:namespace, name: 'gitlab-org')
- project = create(:project, name: 'gitlab-qa', namespace: namespace)
-
- project.add_maintainer(user)
-
- merge_request = create(:merge_request_with_diffs, source_project: project, author: user)
-
- create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: user)
- end
- end
-end
diff --git a/spec/contracts/provider/states/project/merge_request/show_state.rb b/spec/contracts/provider/states/project/merge_request/show_state.rb
new file mode 100644
index 00000000000..46f322f723a
--- /dev/null
+++ b/spec/contracts/provider/states/project/merge_request/show_state.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+Pact.provider_states_for "MergeRequest#show" do
+ provider_state "a merge request with diffs exists" do
+ set_up do
+ user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
+ namespace = create(:namespace, name: 'gitlab-org')
+ project = create(:project, :custom_repo, name: 'gitlab-qa', namespace: namespace, files: {})
+
+ project.add_maintainer(user)
+
+ merge_request = create(:merge_request_with_multiple_diffs, source_project: project)
+ merge_request_diff = create(:merge_request_diff, merge_request: merge_request)
+
+ create(:merge_request_diff_file, :new_file, merge_request_diff: merge_request_diff)
+ end
+ end
+
+ provider_state "a merge request exists" do
+ set_up do
+ user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
+ namespace = create(:namespace, name: 'gitlab-org')
+ project = create(:project, :custom_repo, name: 'gitlab-qa', namespace: namespace, files: {})
+
+ project.add_maintainer(user)
+
+ merge_request = create(:merge_request, source_project: project)
+ merge_request_diff = create(:merge_request_diff, merge_request: merge_request)
+
+ create(:merge_request_diff_file, :new_file, merge_request_diff: merge_request_diff)
+ end
+ end
+
+ provider_state "a merge request with discussions exists" do
+ set_up do
+ user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
+ namespace = create(:namespace, name: 'gitlab-org')
+ project = create(:project, name: 'gitlab-qa', namespace: namespace)
+
+ project.add_maintainer(user)
+
+ merge_request = create(:merge_request_with_diffs, source_project: project, author: user)
+
+ create(:discussion_note_on_merge_request, noteable: merge_request, project: project, author: user)
+ end
+ end
+end
diff --git a/spec/contracts/provider/states/project/pipeline/pipelines_state.rb b/spec/contracts/provider/states/project/pipeline/index_state.rb
index 639c25e9894..639c25e9894 100644
--- a/spec/contracts/provider/states/project/pipeline/pipelines_state.rb
+++ b/spec/contracts/provider/states/project/pipeline/index_state.rb
diff --git a/spec/contracts/provider/states/project/pipeline/pipeline_state.rb b/spec/contracts/provider/states/project/pipeline/pipeline_state.rb
deleted file mode 100644
index d1a4cd34bdd..00000000000
--- a/spec/contracts/provider/states/project/pipeline/pipeline_state.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# frozen_string_literal: true
-
-Pact.provider_states_for "Pipelines#show" do
- provider_state "a pipeline for a project exists" do
- set_up do
- user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
- namespace = create(:namespace, name: 'gitlab-org')
- project = create(:project, :repository, name: 'gitlab-qa', namespace: namespace, creator: user)
- scheduled_job = create(:ci_build, :scheduled)
- manual_job = create(:ci_build, :manual)
-
- project.add_maintainer(user)
-
- create(
- :ci_pipeline,
- :with_job,
- :success,
- iid: 1,
- project: project,
- user: user,
- duration: 10,
- finished_at: '2022-06-01T02:47:31.432Z',
- builds: [scheduled_job, manual_job]
- )
- end
- end
-end
diff --git a/spec/frontend/ide/stores/getters_spec.js b/spec/frontend/ide/stores/getters_spec.js
index 53d161ae5c9..24661e21cd0 100644
--- a/spec/frontend/ide/stores/getters_spec.js
+++ b/spec/frontend/ide/stores/getters_spec.js
@@ -268,7 +268,7 @@ describe('IDE store getters', () => {
currentProject: undefined,
};
- expect(getters.isOnDefaultBranch({}, localGetters)).toBeFalsy();
+ expect(getters.isOnDefaultBranch({}, localGetters)).toBe(undefined);
});
it("returns true when project's default branch matches current branch", () => {
@@ -279,7 +279,7 @@ describe('IDE store getters', () => {
branchName: 'main',
};
- expect(getters.isOnDefaultBranch({}, localGetters)).toBeTruthy();
+ expect(getters.isOnDefaultBranch({}, localGetters)).toBe(true);
});
it("returns false when project's default branch doesn't match current branch", () => {
@@ -290,7 +290,7 @@ describe('IDE store getters', () => {
branchName: 'feature',
};
- expect(getters.isOnDefaultBranch({}, localGetters)).toBeFalsy();
+ expect(getters.isOnDefaultBranch({}, localGetters)).toBe(false);
});
});
diff --git a/spec/frontend/jobs/components/job_log_controllers_spec.js b/spec/frontend/jobs/components/job_log_controllers_spec.js
index cc97d111c06..4075abd24d5 100644
--- a/spec/frontend/jobs/components/job_log_controllers_spec.js
+++ b/spec/frontend/jobs/components/job_log_controllers_spec.js
@@ -1,8 +1,9 @@
import { GlSearchBoxByClick } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
-import { nextTick } from 'vue';
import JobLogControllers from '~/jobs/components/job_log_controllers.vue';
import HelpPopover from '~/vue_shared/components/help_popover.vue';
+import { backoffMockImplementation } from 'helpers/backoff_helper';
+import * as commonUtils from '~/lib/utils/common_utils';
import { mockJobLog } from '../mock_data';
const mockToastShow = jest.fn();
@@ -10,10 +11,15 @@ const mockToastShow = jest.fn();
describe('Job log controllers', () => {
let wrapper;
+ beforeEach(() => {
+ jest.spyOn(commonUtils, 'backOff').mockImplementation(backoffMockImplementation);
+ });
+
afterEach(() => {
if (wrapper?.destroy) {
wrapper.destroy();
}
+ commonUtils.backOff.mockReset();
});
const defaultProps = {
@@ -24,10 +30,11 @@ describe('Job log controllers', () => {
isScrollBottomDisabled: false,
isScrollingDown: true,
isJobLogSizeVisible: true,
+ isComplete: true,
jobLog: mockJobLog,
};
- const createWrapper = (props, jobLogSearch = false) => {
+ const createWrapper = (props, { jobLogSearch = false, jobLogJumpToFailures = false } = {}) => {
wrapper = mount(JobLogControllers, {
propsData: {
...defaultProps,
@@ -36,6 +43,7 @@ describe('Job log controllers', () => {
provide: {
glFeatures: {
jobLogSearch,
+ jobLogJumpToFailures,
},
},
data() {
@@ -58,6 +66,7 @@ describe('Job log controllers', () => {
const findScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]');
const findJobLogSearch = () => wrapper.findComponent(GlSearchBoxByClick);
const findSearchHelp = () => wrapper.findComponent(HelpPopover);
+ const findScrollFailure = () => wrapper.find('[data-testid="job-controller-scroll-to-failure"]');
describe('Truncate information', () => {
describe('with isJobLogSizeVisible', () => {
@@ -109,9 +118,7 @@ describe('Job log controllers', () => {
});
it('emits scrollJobLogTop event on click', async () => {
- findScrollTop().trigger('click');
-
- await nextTick();
+ await findScrollTop().trigger('click');
expect(wrapper.emitted().scrollJobLogTop).toHaveLength(1);
});
@@ -131,9 +138,7 @@ describe('Job log controllers', () => {
});
it('does not emit scrollJobLogTop event on click', async () => {
- findScrollTop().trigger('click');
-
- await nextTick();
+ await findScrollTop().trigger('click');
expect(wrapper.emitted().scrollJobLogTop).toBeUndefined();
});
@@ -147,9 +152,7 @@ describe('Job log controllers', () => {
});
it('emits scrollJobLogBottom event on click', async () => {
- findScrollBottom().trigger('click');
-
- await nextTick();
+ await findScrollBottom().trigger('click');
expect(wrapper.emitted().scrollJobLogBottom).toHaveLength(1);
});
@@ -169,9 +172,7 @@ describe('Job log controllers', () => {
});
it('does not emit scrollJobLogBottom event on click', async () => {
- findScrollBottom().trigger('click');
-
- await nextTick();
+ await findScrollBottom().trigger('click');
expect(wrapper.emitted().scrollJobLogBottom).toBeUndefined();
});
@@ -201,6 +202,91 @@ describe('Job log controllers', () => {
});
});
});
+
+ describe('scroll to failure button', () => {
+ describe('with feature flag disabled', () => {
+ it('does not display button', () => {
+ createWrapper();
+
+ expect(findScrollFailure().exists()).toBe(false);
+ });
+ });
+
+ describe('with red text failures on the page', () => {
+ let firstFailure;
+ let secondFailure;
+
+ beforeEach(() => {
+ jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce(['mock-element']);
+
+ createWrapper({}, { jobLogJumpToFailures: true });
+
+ firstFailure = document.createElement('div');
+ firstFailure.className = 'term-fg-l-red';
+ document.body.appendChild(firstFailure);
+
+ secondFailure = document.createElement('div');
+ secondFailure.className = 'term-fg-l-red';
+ document.body.appendChild(secondFailure);
+ });
+
+ afterEach(() => {
+ if (firstFailure) {
+ firstFailure.remove();
+ firstFailure = null;
+ }
+
+ if (secondFailure) {
+ secondFailure.remove();
+ secondFailure = null;
+ }
+ });
+
+ it('is enabled', () => {
+ expect(findScrollFailure().props('disabled')).toBe(false);
+ });
+
+ it('scrolls to each failure', async () => {
+ jest.spyOn(firstFailure, 'scrollIntoView');
+
+ await findScrollFailure().trigger('click');
+
+ expect(firstFailure.scrollIntoView).toHaveBeenCalled();
+
+ await findScrollFailure().trigger('click');
+
+ expect(secondFailure.scrollIntoView).toHaveBeenCalled();
+
+ await findScrollFailure().trigger('click');
+
+ expect(firstFailure.scrollIntoView).toHaveBeenCalled();
+ });
+ });
+
+ describe('with no red text failures on the page', () => {
+ beforeEach(() => {
+ jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce([]);
+
+ createWrapper({}, { jobLogJumpToFailures: true });
+ });
+
+ it('is disabled', () => {
+ expect(findScrollFailure().props('disabled')).toBe(true);
+ });
+ });
+
+ describe('when the job log is not complete', () => {
+ beforeEach(() => {
+ jest.spyOn(document, 'querySelectorAll').mockReturnValueOnce(['mock-element']);
+
+ createWrapper({ isComplete: false }, { jobLogJumpToFailures: true });
+ });
+
+ it('is enabled', () => {
+ expect(findScrollFailure().props('disabled')).toBe(false);
+ });
+ });
+ });
});
describe('Job log search', () => {
diff --git a/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js b/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js
index d74f959ac0f..281f0f4e4fe 100644
--- a/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_actions_menu_spec.js
@@ -316,7 +316,7 @@ describe('Actions menu', () => {
});
it('is not disabled', () => {
- expect(findStarDashboardItem().attributes('disabled')).toBeFalsy();
+ expect(findStarDashboardItem().attributes('disabled')).toBeUndefined();
});
it('is disabled when starring is taking place', async () => {