Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
fae5be5632
commit
0ac2fcc287
|
@ -177,3 +177,7 @@ overrides:
|
||||||
'@graphql-eslint/no-unused-variables': error
|
'@graphql-eslint/no-unused-variables': error
|
||||||
'@graphql-eslint/no-unused-fragments': error
|
'@graphql-eslint/no-unused-fragments': error
|
||||||
'@graphql-eslint/no-duplicate-fields': error
|
'@graphql-eslint/no-duplicate-fields': error
|
||||||
|
- files:
|
||||||
|
- 'spec/contracts/consumer/**/*'
|
||||||
|
rules:
|
||||||
|
'@gitlab/require-i18n-strings': off
|
||||||
|
|
|
@ -287,6 +287,7 @@ export default {
|
||||||
:is-scroll-top-disabled="isScrollTopDisabled"
|
:is-scroll-top-disabled="isScrollTopDisabled"
|
||||||
:is-job-log-size-visible="isJobLogSizeVisible"
|
:is-job-log-size-visible="isJobLogSizeVisible"
|
||||||
:is-scrolling-down="isScrollingDown"
|
:is-scrolling-down="isScrollingDown"
|
||||||
|
:is-complete="isJobLogComplete"
|
||||||
:job-log="jobLog"
|
:job-log="jobLog"
|
||||||
@scrollJobLogTop="scrollTop"
|
@scrollJobLogTop="scrollTop"
|
||||||
@scrollJobLogBottom="scrollBottom"
|
@scrollJobLogBottom="scrollBottom"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { GlTooltipDirective, GlLink, GlButton, GlSearchBoxByClick } from '@gitlab/ui';
|
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 { numberToHumanSize } from '~/lib/utils/number_utils';
|
||||||
import { __, s__, sprintf } from '~/locale';
|
import { __, s__, sprintf } from '~/locale';
|
||||||
import HelpPopover from '~/vue_shared/components/help_popover.vue';
|
import HelpPopover from '~/vue_shared/components/help_popover.vue';
|
||||||
|
@ -10,6 +10,7 @@ export default {
|
||||||
i18n: {
|
i18n: {
|
||||||
scrollToBottomButtonLabel: s__('Job|Scroll to bottom'),
|
scrollToBottomButtonLabel: s__('Job|Scroll to bottom'),
|
||||||
scrollToTopButtonLabel: s__('Job|Scroll to top'),
|
scrollToTopButtonLabel: s__('Job|Scroll to top'),
|
||||||
|
scrollToNextFailureButtonLabel: s__('Job|Scroll to next failure'),
|
||||||
showRawButtonLabel: s__('Job|Show complete raw'),
|
showRawButtonLabel: s__('Job|Show complete raw'),
|
||||||
searchPlaceholder: s__('Job|Search job log'),
|
searchPlaceholder: s__('Job|Search job log'),
|
||||||
noResults: s__('Job|No search results found'),
|
noResults: s__('Job|No search results found'),
|
||||||
|
@ -55,6 +56,10 @@ export default {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
isComplete: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
jobLog: {
|
jobLog: {
|
||||||
type: Array,
|
type: Array,
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -64,6 +69,8 @@ export default {
|
||||||
return {
|
return {
|
||||||
searchTerm: '',
|
searchTerm: '',
|
||||||
searchResults: [],
|
searchResults: [],
|
||||||
|
failureCount: null,
|
||||||
|
failureIndex: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -75,13 +82,49 @@ export default {
|
||||||
showJobLogSearch() {
|
showJobLogSearch() {
|
||||||
return this.glFeatures.jobLogSearch;
|
return this.glFeatures.jobLogSearch;
|
||||||
},
|
},
|
||||||
|
showJumpToFailures() {
|
||||||
|
return this.glFeatures.jobLogJumpToFailures;
|
||||||
|
},
|
||||||
|
hasFailures() {
|
||||||
|
return this.failureCount > 0;
|
||||||
|
},
|
||||||
|
shouldDisableJumpToFailures() {
|
||||||
|
return !this.hasFailures;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.checkFailureCount();
|
||||||
},
|
},
|
||||||
methods: {
|
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() {
|
handleScrollToTop() {
|
||||||
this.$emit('scrollJobLogTop');
|
this.$emit('scrollJobLogTop');
|
||||||
|
this.failureIndex = 0;
|
||||||
},
|
},
|
||||||
handleScrollToBottom() {
|
handleScrollToBottom() {
|
||||||
this.$emit('scrollJobLogBottom');
|
this.$emit('scrollJobLogBottom');
|
||||||
|
this.failureIndex = 0;
|
||||||
},
|
},
|
||||||
searchJobLog() {
|
searchJobLog() {
|
||||||
this.searchResults = [];
|
this.searchResults = [];
|
||||||
|
@ -135,10 +178,10 @@ export default {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="top-bar">
|
<div class="top-bar gl-display-flex gl-justify-content-space-between">
|
||||||
<!-- truncate information -->
|
<!-- truncate information -->
|
||||||
<div
|
<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"
|
data-testid="log-truncated-info"
|
||||||
>
|
>
|
||||||
<template v-if="isJobLogSizeVisible">
|
<template v-if="isJobLogSizeVisible">
|
||||||
|
@ -154,7 +197,7 @@ export default {
|
||||||
</div>
|
</div>
|
||||||
<!-- eo truncate information -->
|
<!-- eo truncate information -->
|
||||||
|
|
||||||
<div class="controllers gl-float-right">
|
<div class="controllers">
|
||||||
<template v-if="showJobLogSearch">
|
<template v-if="showJobLogSearch">
|
||||||
<gl-search-box-by-click
|
<gl-search-box-by-click
|
||||||
v-model="searchTerm"
|
v-model="searchTerm"
|
||||||
|
@ -187,6 +230,18 @@ export default {
|
||||||
<!-- eo links -->
|
<!-- eo links -->
|
||||||
|
|
||||||
<!-- scroll buttons -->
|
<!-- 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">
|
<div v-gl-tooltip :title="$options.i18n.scrollToTopButtonLabel" class="gl-ml-3">
|
||||||
<gl-button
|
<gl-button
|
||||||
:disabled="isScrollTopDisabled"
|
:disabled="isScrollTopDisabled"
|
||||||
|
|
|
@ -427,10 +427,10 @@
|
||||||
padding-inline-start: 28px;
|
padding-inline-start: 28px;
|
||||||
margin-inline-start: 0 !important;
|
margin-inline-start: 0 !important;
|
||||||
|
|
||||||
> input.task-list-item-checkbox {
|
input.task-list-item-checkbox {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset-inline-start: 8px;
|
inset-inline-start: $gl-padding-8;
|
||||||
top: 5px;
|
inset-block-start: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,17 +15,11 @@
|
||||||
$stroke-size: 1px;
|
$stroke-size: 1px;
|
||||||
|
|
||||||
.right-arrow {
|
.right-arrow {
|
||||||
@include gl-relative;
|
|
||||||
height: $stroke-size;
|
height: $stroke-size;
|
||||||
background-color: var(--gray-900, $gray-900);
|
|
||||||
min-width: $gl-spacing-scale-7;
|
|
||||||
|
|
||||||
&-head {
|
&-head {
|
||||||
@include gl-absolute;
|
top: -2 * $stroke-size;
|
||||||
top: -2*$stroke-size;
|
left: calc(100% - #{5 * $stroke-size});
|
||||||
left: calc(100% - #{5*$stroke-size});
|
|
||||||
@include gl-p-1;
|
|
||||||
@include gl-border-solid;
|
|
||||||
border-width: 0 $stroke-size $stroke-size 0;
|
border-width: 0 $stroke-size $stroke-size 0;
|
||||||
border-color: var(--gray-900, $gray-900);
|
border-color: var(--gray-900, $gray-900);
|
||||||
transform: rotate(-45deg);
|
transform: rotate(-45deg);
|
||||||
|
@ -41,14 +35,10 @@ $stroke-size: 1px;
|
||||||
.rule-condition {
|
.rule-condition {
|
||||||
@media (min-width: $breakpoint-lg) {
|
@media (min-width: $breakpoint-lg) {
|
||||||
flex-basis: 25%;
|
flex-basis: 25%;
|
||||||
flex-shrink: 0;
|
@include gl-flex-shrink-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: $breakpoint-lg) {
|
@media (max-width: $breakpoint-lg) {
|
||||||
@include gl-w-full;
|
@include gl-w-full;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.rule-action {
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,77 +1,24 @@
|
||||||
@import 'mixins_and_variables_and_functions';
|
@import 'mixins_and_variables_and_functions';
|
||||||
|
|
||||||
.description {
|
.description {
|
||||||
ul,
|
ul.task-list > li.task-list-item {
|
||||||
ol {
|
margin-inline-start: 0.5rem !important; /* Override typography.scss */
|
||||||
/* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
li {
|
||||||
position: relative;
|
position: relative;
|
||||||
/* In the browser, the li element comes after (to the right of) the bullet point, so hovering
|
margin-inline-start: 2.25rem;
|
||||||
* 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. */
|
&.task-list-item > .drag-icon {
|
||||||
margin-inline-start: -1rem;
|
inset-inline-start: -0.6rem;
|
||||||
padding-inline-start: 2.5rem;
|
}
|
||||||
|
|
||||||
.drag-icon {
|
.drag-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset-block-start: 0.3rem;
|
inset-block-start: 0.3rem;
|
||||||
inset-inline-start: 1rem;
|
inset-inline-start: -2.3rem;
|
||||||
}
|
padding-inline-end: 1rem;
|
||||||
|
width: 2rem;
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ class Projects::JobsController < Projects::ApplicationController
|
||||||
before_action :authorize_create_proxy_build!, only: :proxy_websocket_authorize
|
before_action :authorize_create_proxy_build!, only: :proxy_websocket_authorize
|
||||||
before_action :verify_proxy_request!, 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_search, only: [:show]
|
||||||
|
before_action :push_job_log_jump_to_failures, only: [:show]
|
||||||
before_action :reject_if_build_artifacts_size_refreshing!, only: [:erase]
|
before_action :reject_if_build_artifacts_size_refreshing!, only: [:erase]
|
||||||
|
|
||||||
layout 'project'
|
layout 'project'
|
||||||
|
@ -252,4 +253,8 @@ class Projects::JobsController < Projects::ApplicationController
|
||||||
def push_job_log_search
|
def push_job_log_search
|
||||||
push_frontend_feature_flag(:job_log_search, @project)
|
push_frontend_feature_flag(:job_log_search, @project)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def push_job_log_jump_to_failures
|
||||||
|
push_frontend_feature_flag(:job_log_jump_to_failures, @project)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
|
@ -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. |
|
| `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).|
|
| `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. |
|
| `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. |
|
| `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).
|
The assignable `user_id` are the users who belong to the given group with the Maintainer role (or above).
|
||||||
|
|
|
@ -709,7 +709,6 @@ args: {
|
||||||
security: {
|
security: {
|
||||||
authn_requests_signed: true, # enable signature on AuthNRequest
|
authn_requests_signed: true, # enable signature on AuthNRequest
|
||||||
want_assertions_signed: true, # enable the requirement of signed assertion
|
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
|
metadata_signed: false, # enable signature on Metadata
|
||||||
signature_method: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
|
signature_method: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
|
||||||
digest_method: 'http://www.w3.org/2001/04/xmlenc#sha256',
|
digest_method: 'http://www.w3.org/2001/04/xmlenc#sha256',
|
||||||
|
|
|
@ -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. |
|
| `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_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_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.) |
|
| `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_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`. |
|
| `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`. |
|
||||||
|
|
|
@ -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.
|
|
@ -4,31 +4,30 @@ return if Rails.env.production?
|
||||||
|
|
||||||
require 'pact/tasks/verification_task'
|
require 'pact/tasks/verification_task'
|
||||||
|
|
||||||
contracts = File.expand_path('../../../spec/contracts', __dir__)
|
contracts = File.expand_path('../../../spec/contracts/contracts/project/merge_request', __dir__)
|
||||||
provider = File.expand_path('provider', contracts)
|
provider = File.expand_path('../../../spec/contracts/provider', __dir__)
|
||||||
|
|
||||||
# rubocop:disable Rails/RakeEnvironment
|
# rubocop:disable Rails/RakeEnvironment
|
||||||
namespace :contracts do
|
namespace :contracts do
|
||||||
namespace :merge_requests do
|
namespace :merge_requests do
|
||||||
Pact::VerificationTask.new(:diffs_batch) do |pact|
|
Pact::VerificationTask.new(:diffs_batch) do |pact|
|
||||||
pact.uri(
|
pact.uri(
|
||||||
"#{contracts}/contracts/project/merge_request/show/mergerequest#show-merge_request_diffs_batch_endpoint.json",
|
"#{contracts}/show/mergerequest#show-merge_request_diffs_batch_endpoint.json",
|
||||||
pact_helper: "#{provider}/pact_helpers/project/merge_request/diffs_batch_helper.rb"
|
pact_helper: "#{provider}/pact_helpers/project/merge_request/show/diffs_batch_helper.rb"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
Pact::VerificationTask.new(:diffs_metadata) do |pact|
|
Pact::VerificationTask.new(:diffs_metadata) do |pact|
|
||||||
pact.uri(
|
pact.uri(
|
||||||
"#{contracts}/contracts/project/merge_request/show/" \
|
"#{contracts}/show/mergerequest#show-merge_request_diffs_metadata_endpoint.json",
|
||||||
"mergerequest#show-merge_request_diffs_metadata_endpoint.json",
|
pact_helper: "#{provider}/pact_helpers/project/merge_request/show/diffs_metadata_helper.rb"
|
||||||
pact_helper: "#{provider}/pact_helpers/project/merge_request/diffs_metadata_helper.rb"
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
Pact::VerificationTask.new(:discussions) do |pact|
|
Pact::VerificationTask.new(:discussions) do |pact|
|
||||||
pact.uri(
|
pact.uri(
|
||||||
"#{contracts}/contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json",
|
"#{contracts}/show/mergerequest#show-merge_request_discussions_endpoint.json",
|
||||||
pact_helper: "#{provider}/pact_helpers/project/merge_request/discussions_helper.rb"
|
pact_helper: "#{provider}/pact_helpers/project/merge_request/show/discussions_helper.rb"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ return if Rails.env.production?
|
||||||
require 'pact/tasks/verification_task'
|
require 'pact/tasks/verification_task'
|
||||||
|
|
||||||
contracts = File.expand_path('../../../spec/contracts/contracts/project/pipeline_schedule', __dir__)
|
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
|
# rubocop:disable Rails/RakeEnvironment
|
||||||
namespace :contracts do
|
namespace :contracts do
|
||||||
|
|
|
@ -4,36 +4,36 @@ return if Rails.env.production?
|
||||||
|
|
||||||
require 'pact/tasks/verification_task'
|
require 'pact/tasks/verification_task'
|
||||||
|
|
||||||
contracts = File.expand_path('../../../spec/contracts', __dir__)
|
contracts = File.expand_path('../../../spec/contracts/contracts/project/pipeline', __dir__)
|
||||||
provider = File.expand_path('provider', contracts)
|
provider = File.expand_path('../../../spec/contracts/provider', __dir__)
|
||||||
|
|
||||||
# rubocop:disable Rails/RakeEnvironment
|
# rubocop:disable Rails/RakeEnvironment
|
||||||
namespace :contracts do
|
namespace :contracts do
|
||||||
namespace :pipelines do
|
namespace :pipelines do
|
||||||
Pact::VerificationTask.new(:create_a_new_pipeline) do |pact|
|
Pact::VerificationTask.new(:create_a_new_pipeline) do |pact|
|
||||||
pact.uri(
|
pact.uri(
|
||||||
"#{contracts}/contracts/project/pipeline/new/pipelines#new-post_create_a_new_pipeline.json",
|
"#{contracts}/new/pipelines#new-post_create_a_new_pipeline.json",
|
||||||
pact_helper: "#{provider}/pact_helpers/project/pipeline/create_a_new_pipeline_helper.rb"
|
pact_helper: "#{provider}/pact_helpers/project/pipeline/index/create_a_new_pipeline_helper.rb"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
Pact::VerificationTask.new(:get_list_project_pipelines) do |pact|
|
Pact::VerificationTask.new(:get_list_project_pipelines) do |pact|
|
||||||
pact.uri(
|
pact.uri(
|
||||||
"#{contracts}/contracts/project/pipeline/index/pipelines#index-get_list_project_pipelines.json",
|
"#{contracts}/index/pipelines#index-get_list_project_pipelines.json",
|
||||||
pact_helper: "#{provider}/pact_helpers/project/pipeline/get_list_project_pipelines_helper.rb"
|
pact_helper: "#{provider}/pact_helpers/project/pipeline/index/get_list_project_pipelines_helper.rb"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
Pact::VerificationTask.new(:get_pipeline_header_data) do |pact|
|
Pact::VerificationTask.new(:get_pipeline_header_data) do |pact|
|
||||||
pact.uri(
|
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"
|
pact_helper: "#{provider}/pact_helpers/project/pipeline/show/get_pipeline_header_data_helper.rb"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
Pact::VerificationTask.new(:delete_pipeline) do |pact|
|
Pact::VerificationTask.new(:delete_pipeline) do |pact|
|
||||||
pact.uri(
|
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"
|
pact_helper: "#{provider}/pact_helpers/project/pipeline/show/delete_pipeline_helper.rb"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -22656,6 +22656,9 @@ msgstr ""
|
||||||
msgid "Job|Scroll to bottom"
|
msgid "Job|Scroll to bottom"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Job|Scroll to next failure"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
msgid "Job|Scroll to top"
|
msgid "Job|Scroll to top"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { Matchers } from '@pact-foundation/pact';
|
import { Matchers } from '@pact-foundation/pact';
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
|
@ -73,8 +71,12 @@ const DiffsBatch = {
|
||||||
body,
|
body,
|
||||||
},
|
},
|
||||||
|
|
||||||
request: {
|
scenario: {
|
||||||
|
state: 'a merge request with diffs exists',
|
||||||
uponReceiving: 'a request for diff lines',
|
uponReceiving: 'a request for diff lines',
|
||||||
|
},
|
||||||
|
|
||||||
|
request: {
|
||||||
withRequest: {
|
withRequest: {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json',
|
path: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json',
|
||||||
|
@ -87,5 +89,3 @@ const DiffsBatch = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export { DiffsBatch };
|
export { DiffsBatch };
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { Matchers } from '@pact-foundation/pact';
|
import { Matchers } from '@pact-foundation/pact';
|
||||||
|
|
||||||
const body = {
|
const body = {
|
||||||
|
@ -81,8 +79,12 @@ const DiffsMetadata = {
|
||||||
body,
|
body,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
scenario: {
|
||||||
|
state: 'a merge request exists',
|
||||||
|
uponReceiving: 'a request for diffs metadata',
|
||||||
|
},
|
||||||
|
|
||||||
request: {
|
request: {
|
||||||
uponReceiving: 'a request for Diffs Metadata',
|
|
||||||
withRequest: {
|
withRequest: {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json',
|
path: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json',
|
||||||
|
@ -94,5 +96,3 @@ const DiffsMetadata = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export { DiffsMetadata };
|
export { DiffsMetadata };
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { Matchers } from '@pact-foundation/pact';
|
import { Matchers } from '@pact-foundation/pact';
|
||||||
|
|
||||||
const body = Matchers.eachLike({
|
const body = Matchers.eachLike({
|
||||||
|
@ -70,8 +68,12 @@ const Discussions = {
|
||||||
body,
|
body,
|
||||||
},
|
},
|
||||||
|
|
||||||
request: {
|
scenario: {
|
||||||
|
state: 'a merge request with discussions exists',
|
||||||
uponReceiving: 'a request for discussions',
|
uponReceiving: 'a request for discussions',
|
||||||
|
},
|
||||||
|
|
||||||
|
request: {
|
||||||
withRequest: {
|
withRequest: {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json',
|
path: '/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json',
|
||||||
|
@ -83,5 +85,3 @@ const Discussions = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export { Discussions };
|
export { Discussions };
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { Matchers } from '@pact-foundation/pact';
|
import { Matchers } from '@pact-foundation/pact';
|
||||||
import { REDIRECT_HTML } from '../../../helpers/common_regex_patterns';
|
import { REDIRECT_HTML } from '../../../helpers/common_regex_patterns';
|
||||||
|
|
||||||
|
@ -39,5 +37,3 @@ const NewProjectPipeline = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export { NewProjectPipeline };
|
export { NewProjectPipeline };
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -6,6 +6,11 @@ const DeletePipeline = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
scenario: {
|
||||||
|
state: 'a pipeline for a project exists',
|
||||||
|
uponReceiving: 'a request to delete the pipeline',
|
||||||
|
},
|
||||||
|
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
path: '/api/graphql',
|
path: '/api/graphql',
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { Matchers } from '@pact-foundation/pact';
|
import { Matchers } from '@pact-foundation/pact';
|
||||||
import {
|
import {
|
||||||
URL,
|
URL,
|
||||||
|
@ -225,8 +223,12 @@ const ProjectPipelines = {
|
||||||
body,
|
body,
|
||||||
},
|
},
|
||||||
|
|
||||||
request: {
|
scenario: {
|
||||||
|
state: 'a few pipelines for a project exists',
|
||||||
uponReceiving: 'a request for a list of project pipelines',
|
uponReceiving: 'a request for a list of project pipelines',
|
||||||
|
},
|
||||||
|
|
||||||
|
request: {
|
||||||
withRequest: {
|
withRequest: {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: '/gitlab-org/gitlab-qa/-/pipelines.json',
|
path: '/gitlab-org/gitlab-qa/-/pipelines.json',
|
||||||
|
@ -239,5 +241,3 @@ const ProjectPipelines = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export { ProjectPipelines };
|
export { ProjectPipelines };
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { Matchers } from '@pact-foundation/pact';
|
import { Matchers } from '@pact-foundation/pact';
|
||||||
import {
|
import {
|
||||||
JOB_STATUSES,
|
JOB_STATUSES,
|
||||||
|
@ -83,6 +81,11 @@ const PipelineHeaderData = {
|
||||||
body,
|
body,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
scenario: {
|
||||||
|
state: 'a pipeline for a project exists',
|
||||||
|
uponReceiving: 'a request for the pipeline header data',
|
||||||
|
},
|
||||||
|
|
||||||
request: {
|
request: {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
path: '/api/graphql',
|
path: '/api/graphql',
|
||||||
|
@ -95,5 +98,3 @@ const PipelineHeaderData = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export { PipelineHeaderData };
|
export { PipelineHeaderData };
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { Matchers } from '@pact-foundation/pact';
|
import { Matchers } from '@pact-foundation/pact';
|
||||||
import { REDIRECT_HTML } from '../../../helpers/common_regex_patterns';
|
import { REDIRECT_HTML } from '../../../helpers/common_regex_patterns';
|
||||||
|
|
||||||
|
@ -44,5 +42,3 @@ const UpdatePipelineSchedule = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export { UpdatePipelineSchedule };
|
export { UpdatePipelineSchedule };
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { request } from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
export function getDiffsMetadata(endpoint) {
|
export async function getDiffsMetadata(endpoint) {
|
||||||
const { url } = endpoint;
|
const { url } = endpoint;
|
||||||
|
|
||||||
return request({
|
return axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
baseURL: url,
|
baseURL: url,
|
||||||
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json',
|
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_metadata.json',
|
||||||
|
@ -11,10 +11,10 @@ export function getDiffsMetadata(endpoint) {
|
||||||
}).then((response) => response.data);
|
}).then((response) => response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDiscussions(endpoint) {
|
export async function getDiscussions(endpoint) {
|
||||||
const { url } = endpoint;
|
const { url } = endpoint;
|
||||||
|
|
||||||
return request({
|
return axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
baseURL: url,
|
baseURL: url,
|
||||||
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json',
|
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/discussions.json',
|
||||||
|
@ -22,10 +22,10 @@ export function getDiscussions(endpoint) {
|
||||||
}).then((response) => response.data);
|
}).then((response) => response.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDiffsBatch(endpoint) {
|
export async function getDiffsBatch(endpoint) {
|
||||||
const { url } = endpoint;
|
const { url } = endpoint;
|
||||||
|
|
||||||
return request({
|
return axios({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
baseURL: url,
|
baseURL: url,
|
||||||
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json?page=0',
|
url: '/gitlab-org/gitlab-qa/-/merge_requests/1/diffs_batch.json?page=0',
|
|
@ -1,5 +1,20 @@
|
||||||
import axios from 'axios';
|
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) {
|
export async function postProjectPipelines(endpoint) {
|
||||||
const { url } = endpoint;
|
const { url } = endpoint;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { pactWith } from 'jest-pact';
|
import { pactWith } from 'jest-pact';
|
||||||
|
|
||||||
import { DiffsBatch } from '../../../fixtures/project/merge_request/diffs_batch.fixture';
|
import { DiffsBatch } from '../../../fixtures/project/merge_request/diffs_batch.fixture';
|
||||||
|
@ -9,7 +7,7 @@ import {
|
||||||
getDiffsBatch,
|
getDiffsBatch,
|
||||||
getDiffsMetadata,
|
getDiffsMetadata,
|
||||||
getDiscussions,
|
getDiscussions,
|
||||||
} from '../../../endpoints/project/merge_requests';
|
} from '../../../resources/api/project/merge_requests';
|
||||||
|
|
||||||
const CONSUMER_NAME = 'MergeRequest#show';
|
const CONSUMER_NAME = 'MergeRequest#show';
|
||||||
const CONSUMER_LOG = '../logs/consumer.log';
|
const CONSUMER_LOG = '../logs/consumer.log';
|
||||||
|
@ -31,19 +29,19 @@ pactWith(
|
||||||
describe(DIFFS_BATCH_PROVIDER_NAME, () => {
|
describe(DIFFS_BATCH_PROVIDER_NAME, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const interaction = {
|
const interaction = {
|
||||||
state: 'a merge request with diffs exists',
|
...DiffsBatch.scenario,
|
||||||
...DiffsBatch.request,
|
...DiffsBatch.request,
|
||||||
willRespondWith: DiffsBatch.success,
|
willRespondWith: DiffsBatch.success,
|
||||||
};
|
};
|
||||||
provider.addInteraction(interaction);
|
provider.addInteraction(interaction);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a successful body', () => {
|
it('returns a successful body', async () => {
|
||||||
return getDiffsBatch({
|
const diffsBatch = await getDiffsBatch({
|
||||||
url: provider.mockService.baseUrl,
|
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, () => {
|
describe(DISCUSSIONS_PROVIDER_NAME, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const interaction = {
|
const interaction = {
|
||||||
state: 'a merge request with discussions exists',
|
...Discussions.scenario,
|
||||||
...Discussions.request,
|
...Discussions.request,
|
||||||
willRespondWith: Discussions.success,
|
willRespondWith: Discussions.success,
|
||||||
};
|
};
|
||||||
provider.addInteraction(interaction);
|
provider.addInteraction(interaction);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('return a successful body', () => {
|
it('return a successful body', async () => {
|
||||||
return getDiscussions({
|
const discussions = await getDiscussions({
|
||||||
url: provider.mockService.baseUrl,
|
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, () => {
|
describe(DIFFS_METADATA_PROVIDER_NAME, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const interaction = {
|
const interaction = {
|
||||||
state: 'a merge request exists',
|
...DiffsMetadata.scenario,
|
||||||
...DiffsMetadata.request,
|
...DiffsMetadata.request,
|
||||||
willRespondWith: DiffsMetadata.success,
|
willRespondWith: DiffsMetadata.success,
|
||||||
};
|
};
|
||||||
provider.addInteraction(interaction);
|
provider.addInteraction(interaction);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('return a successful body', () => {
|
it('return a successful body', async () => {
|
||||||
return getDiffsMetadata({
|
const diffsMetadata = await getDiffsMetadata({
|
||||||
url: provider.mockService.baseUrl,
|
url: provider.mockService.baseUrl,
|
||||||
}).then((diffsMetadata) => {
|
|
||||||
expect(diffsMetadata).toEqual(DiffsMetadata.body);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(diffsMetadata).toEqual(DiffsMetadata.body);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { pactWith } from 'jest-pact';
|
import { pactWith } from 'jest-pact';
|
||||||
|
|
||||||
import { ProjectPipelines } from '../../../fixtures/project/pipeline/get_list_project_pipelines.fixture';
|
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_NAME = 'Pipelines#index';
|
||||||
const CONSUMER_LOG = '../logs/consumer.log';
|
const CONSUMER_LOG = '../logs/consumer.log';
|
||||||
|
@ -23,22 +21,20 @@ pactWith(
|
||||||
describe(PROVIDER_NAME, () => {
|
describe(PROVIDER_NAME, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const interaction = {
|
const interaction = {
|
||||||
state: 'a few pipelines for a project exists',
|
...ProjectPipelines.scenario,
|
||||||
...ProjectPipelines.request,
|
...ProjectPipelines.request,
|
||||||
willRespondWith: ProjectPipelines.success,
|
willRespondWith: ProjectPipelines.success,
|
||||||
};
|
};
|
||||||
provider.addInteraction(interaction);
|
provider.addInteraction(interaction);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns a successful body', () => {
|
it('returns a successful body', async () => {
|
||||||
return getProjectPipelines({
|
const pipelines = await getProjectPipelines({
|
||||||
url: provider.mockService.baseUrl,
|
url: provider.mockService.baseUrl,
|
||||||
}).then((pipelines) => {
|
|
||||||
expect(pipelines).toEqual(ProjectPipelines.body);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(pipelines).toEqual(ProjectPipelines.body);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { pactWith } from 'jest-pact';
|
import { pactWith } from 'jest-pact';
|
||||||
|
|
||||||
import { NewProjectPipeline } from '../../../fixtures/project/pipeline/create_a_new_pipeline.fixture';
|
import { NewProjectPipeline } from '../../../fixtures/project/pipeline/create_a_new_pipeline.fixture';
|
||||||
|
@ -41,5 +39,3 @@ pactWith(
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { pactWith } from 'jest-pact';
|
import { pactWith } from 'jest-pact';
|
||||||
import { GraphQLInteraction } from '@pact-foundation/pact';
|
import { GraphQLInteraction } from '@pact-foundation/pact';
|
||||||
|
|
||||||
|
@ -32,8 +30,8 @@ pactWith(
|
||||||
'app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql',
|
'app/assets/javascripts/pipelines/graphql/queries/get_pipeline_header_data.query.graphql',
|
||||||
);
|
);
|
||||||
const graphqlQuery = new GraphQLInteraction()
|
const graphqlQuery = new GraphQLInteraction()
|
||||||
.given('a pipeline for a project exists')
|
.given(PipelineHeaderData.scenario.state)
|
||||||
.uponReceiving('a request for the pipeline header data')
|
.uponReceiving(PipelineHeaderData.scenario.uponReceiving)
|
||||||
.withQuery(query)
|
.withQuery(query)
|
||||||
.withRequest(PipelineHeaderData.request)
|
.withRequest(PipelineHeaderData.request)
|
||||||
.withVariables(PipelineHeaderData.variables)
|
.withVariables(PipelineHeaderData.variables)
|
||||||
|
@ -69,8 +67,8 @@ pactWith(
|
||||||
'app/assets/javascripts/pipelines/graphql/mutations/delete_pipeline.mutation.graphql',
|
'app/assets/javascripts/pipelines/graphql/mutations/delete_pipeline.mutation.graphql',
|
||||||
);
|
);
|
||||||
const graphqlQuery = new GraphQLInteraction()
|
const graphqlQuery = new GraphQLInteraction()
|
||||||
.given('a pipeline for a project exists')
|
.given(DeletePipeline.scenario.state)
|
||||||
.uponReceiving('a request to delete the pipeline')
|
.uponReceiving(DeletePipeline.scenario.uponReceiving)
|
||||||
.withQuery(query)
|
.withQuery(query)
|
||||||
.withRequest(DeletePipeline.request)
|
.withRequest(DeletePipeline.request)
|
||||||
.withVariables(DeletePipeline.variables)
|
.withVariables(DeletePipeline.variables)
|
||||||
|
@ -89,5 +87,3 @@ pactWith(
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/* eslint-disable @gitlab/require-i18n-strings */
|
|
||||||
|
|
||||||
import { pactWith } from 'jest-pact';
|
import { pactWith } from 'jest-pact';
|
||||||
|
|
||||||
import { UpdatePipelineSchedule } from '../../../fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture';
|
import { UpdatePipelineSchedule } from '../../../fixtures/project/pipeline_schedule/update_pipeline_schedule.fixture';
|
||||||
|
@ -41,5 +39,3 @@ pactWith(
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
/* eslint-enable @gitlab/require-i18n-strings */
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
},
|
},
|
||||||
"interactions": [
|
"interactions": [
|
||||||
{
|
{
|
||||||
"description": "a request for Diffs Metadata",
|
"description": "a request for diffs metadata",
|
||||||
"providerState": "a merge request exists",
|
"providerState": "a merge request exists",
|
||||||
"request": {
|
"request": {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative '../../../spec_helper'
|
require_relative '../../../../spec_helper'
|
||||||
require_relative '../../../states/project/merge_request/diffs_batch_state'
|
require_relative '../../../../states/project/merge_request/show_state'
|
||||||
|
|
||||||
module Provider
|
module Provider
|
||||||
module DiffsBatchHelper
|
module DiffsBatchHelper
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative '../../../spec_helper'
|
require_relative '../../../../spec_helper'
|
||||||
require_relative '../../../states/project/merge_request/diffs_metadata_state'
|
require_relative '../../../../states/project/merge_request/show_state'
|
||||||
|
|
||||||
module Provider
|
module Provider
|
||||||
module DiffsMetadataHelper
|
module DiffsMetadataHelper
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative '../../../spec_helper'
|
require_relative '../../../../spec_helper'
|
||||||
require_relative '../../../states/project/merge_request/discussions_state'
|
require_relative '../../../../states/project/merge_request/show_state'
|
||||||
|
|
||||||
module Provider
|
module Provider
|
||||||
module DiscussionsHelper
|
module DiscussionsHelper
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative '../../../spec_helper'
|
require_relative '../../../../spec_helper'
|
||||||
require_relative '../../../states/project/pipeline/new_state'
|
require_relative '../../../../states/project/pipeline/new_state'
|
||||||
|
|
||||||
module Provider
|
module Provider
|
||||||
module CreateNewPipelineHelper
|
module CreateNewPipelineHelper
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative '../../../spec_helper'
|
require_relative '../../../../spec_helper'
|
||||||
require_relative '../../../states/project/pipeline/pipelines_state'
|
require_relative '../../../../states/project/pipeline/index_state'
|
||||||
|
|
||||||
module Provider
|
module Provider
|
||||||
module GetListProjectPipelinesHelper
|
module GetListProjectPipelinesHelper
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative '../../../../spec_helper'
|
require_relative '../../../../spec_helper'
|
||||||
require_relative '../../../../states/project/pipeline/pipeline_state'
|
require_relative '../../../../states/project/pipeline/show_state'
|
||||||
|
|
||||||
module Provider
|
module Provider
|
||||||
module GetPipelinesHeaderDataHelper
|
module GetPipelinesHeaderDataHelper
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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
|
|
@ -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
|
|
|
@ -268,7 +268,7 @@ describe('IDE store getters', () => {
|
||||||
currentProject: undefined,
|
currentProject: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(getters.isOnDefaultBranch({}, localGetters)).toBeFalsy();
|
expect(getters.isOnDefaultBranch({}, localGetters)).toBe(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns true when project's default branch matches current branch", () => {
|
it("returns true when project's default branch matches current branch", () => {
|
||||||
|
@ -279,7 +279,7 @@ describe('IDE store getters', () => {
|
||||||
branchName: 'main',
|
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", () => {
|
it("returns false when project's default branch doesn't match current branch", () => {
|
||||||
|
@ -290,7 +290,7 @@ describe('IDE store getters', () => {
|
||||||
branchName: 'feature',
|
branchName: 'feature',
|
||||||
};
|
};
|
||||||
|
|
||||||
expect(getters.isOnDefaultBranch({}, localGetters)).toBeFalsy();
|
expect(getters.isOnDefaultBranch({}, localGetters)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { GlSearchBoxByClick } from '@gitlab/ui';
|
import { GlSearchBoxByClick } from '@gitlab/ui';
|
||||||
import { mount } from '@vue/test-utils';
|
import { mount } from '@vue/test-utils';
|
||||||
import { nextTick } from 'vue';
|
|
||||||
import JobLogControllers from '~/jobs/components/job_log_controllers.vue';
|
import JobLogControllers from '~/jobs/components/job_log_controllers.vue';
|
||||||
import HelpPopover from '~/vue_shared/components/help_popover.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';
|
import { mockJobLog } from '../mock_data';
|
||||||
|
|
||||||
const mockToastShow = jest.fn();
|
const mockToastShow = jest.fn();
|
||||||
|
@ -10,10 +11,15 @@ const mockToastShow = jest.fn();
|
||||||
describe('Job log controllers', () => {
|
describe('Job log controllers', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.spyOn(commonUtils, 'backOff').mockImplementation(backoffMockImplementation);
|
||||||
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
if (wrapper?.destroy) {
|
if (wrapper?.destroy) {
|
||||||
wrapper.destroy();
|
wrapper.destroy();
|
||||||
}
|
}
|
||||||
|
commonUtils.backOff.mockReset();
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
|
@ -24,10 +30,11 @@ describe('Job log controllers', () => {
|
||||||
isScrollBottomDisabled: false,
|
isScrollBottomDisabled: false,
|
||||||
isScrollingDown: true,
|
isScrollingDown: true,
|
||||||
isJobLogSizeVisible: true,
|
isJobLogSizeVisible: true,
|
||||||
|
isComplete: true,
|
||||||
jobLog: mockJobLog,
|
jobLog: mockJobLog,
|
||||||
};
|
};
|
||||||
|
|
||||||
const createWrapper = (props, jobLogSearch = false) => {
|
const createWrapper = (props, { jobLogSearch = false, jobLogJumpToFailures = false } = {}) => {
|
||||||
wrapper = mount(JobLogControllers, {
|
wrapper = mount(JobLogControllers, {
|
||||||
propsData: {
|
propsData: {
|
||||||
...defaultProps,
|
...defaultProps,
|
||||||
|
@ -36,6 +43,7 @@ describe('Job log controllers', () => {
|
||||||
provide: {
|
provide: {
|
||||||
glFeatures: {
|
glFeatures: {
|
||||||
jobLogSearch,
|
jobLogSearch,
|
||||||
|
jobLogJumpToFailures,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@ -58,6 +66,7 @@ describe('Job log controllers', () => {
|
||||||
const findScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]');
|
const findScrollBottom = () => wrapper.find('[data-testid="job-controller-scroll-bottom"]');
|
||||||
const findJobLogSearch = () => wrapper.findComponent(GlSearchBoxByClick);
|
const findJobLogSearch = () => wrapper.findComponent(GlSearchBoxByClick);
|
||||||
const findSearchHelp = () => wrapper.findComponent(HelpPopover);
|
const findSearchHelp = () => wrapper.findComponent(HelpPopover);
|
||||||
|
const findScrollFailure = () => wrapper.find('[data-testid="job-controller-scroll-to-failure"]');
|
||||||
|
|
||||||
describe('Truncate information', () => {
|
describe('Truncate information', () => {
|
||||||
describe('with isJobLogSizeVisible', () => {
|
describe('with isJobLogSizeVisible', () => {
|
||||||
|
@ -109,9 +118,7 @@ describe('Job log controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emits scrollJobLogTop event on click', async () => {
|
it('emits scrollJobLogTop event on click', async () => {
|
||||||
findScrollTop().trigger('click');
|
await findScrollTop().trigger('click');
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
expect(wrapper.emitted().scrollJobLogTop).toHaveLength(1);
|
expect(wrapper.emitted().scrollJobLogTop).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
@ -131,9 +138,7 @@ describe('Job log controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not emit scrollJobLogTop event on click', async () => {
|
it('does not emit scrollJobLogTop event on click', async () => {
|
||||||
findScrollTop().trigger('click');
|
await findScrollTop().trigger('click');
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
expect(wrapper.emitted().scrollJobLogTop).toBeUndefined();
|
expect(wrapper.emitted().scrollJobLogTop).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
@ -147,9 +152,7 @@ describe('Job log controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emits scrollJobLogBottom event on click', async () => {
|
it('emits scrollJobLogBottom event on click', async () => {
|
||||||
findScrollBottom().trigger('click');
|
await findScrollBottom().trigger('click');
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
expect(wrapper.emitted().scrollJobLogBottom).toHaveLength(1);
|
expect(wrapper.emitted().scrollJobLogBottom).toHaveLength(1);
|
||||||
});
|
});
|
||||||
|
@ -169,9 +172,7 @@ describe('Job log controllers', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not emit scrollJobLogBottom event on click', async () => {
|
it('does not emit scrollJobLogBottom event on click', async () => {
|
||||||
findScrollBottom().trigger('click');
|
await findScrollBottom().trigger('click');
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
|
|
||||||
expect(wrapper.emitted().scrollJobLogBottom).toBeUndefined();
|
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', () => {
|
describe('Job log search', () => {
|
||||||
|
|
|
@ -316,7 +316,7 @@ describe('Actions menu', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is not disabled', () => {
|
it('is not disabled', () => {
|
||||||
expect(findStarDashboardItem().attributes('disabled')).toBeFalsy();
|
expect(findStarDashboardItem().attributes('disabled')).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('is disabled when starring is taking place', async () => {
|
it('is disabled when starring is taking place', async () => {
|
||||||
|
|
Loading…
Reference in New Issue