Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
3825437c53
commit
968e01a6dd
21 changed files with 397 additions and 109 deletions
|
@ -2,3 +2,4 @@
|
||||||
|
|
||||||
export const ESC_KEY = 'Escape';
|
export const ESC_KEY = 'Escape';
|
||||||
export const ESC_KEY_IE11 = 'Esc'; // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
|
export const ESC_KEY_IE11 = 'Esc'; // https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key
|
||||||
|
export const ENTER_KEY = 'Enter';
|
||||||
|
|
|
@ -85,7 +85,7 @@ export default {
|
||||||
saveButtonLabel() {
|
saveButtonLabel() {
|
||||||
return this.isExistingRelease ? __('Save changes') : __('Create release');
|
return this.isExistingRelease ? __('Save changes') : __('Create release');
|
||||||
},
|
},
|
||||||
isSaveChangesDisabled() {
|
isFormSubmissionDisabled() {
|
||||||
return this.isUpdatingRelease || !this.isValid;
|
return this.isUpdatingRelease || !this.isValid;
|
||||||
},
|
},
|
||||||
milestoneComboboxExtraLinks() {
|
milestoneComboboxExtraLinks() {
|
||||||
|
@ -116,13 +116,18 @@ export default {
|
||||||
'updateReleaseNotes',
|
'updateReleaseNotes',
|
||||||
'updateReleaseMilestones',
|
'updateReleaseMilestones',
|
||||||
]),
|
]),
|
||||||
|
submitForm() {
|
||||||
|
if (!this.isFormSubmissionDisabled) {
|
||||||
|
this.saveRelease();
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="d-flex flex-column">
|
<div class="d-flex flex-column">
|
||||||
<p class="pt-3 js-subtitle-text" v-html="subtitleText"></p>
|
<p class="pt-3 js-subtitle-text" v-html="subtitleText"></p>
|
||||||
<form v-if="showForm" @submit.prevent="saveRelease()">
|
<form v-if="showForm" class="js-quick-submit" @submit.prevent="submitForm">
|
||||||
<tag-field />
|
<tag-field />
|
||||||
<gl-form-group>
|
<gl-form-group>
|
||||||
<label for="release-title">{{ __('Release title') }}</label>
|
<label for="release-title">{{ __('Release title') }}</label>
|
||||||
|
@ -134,7 +139,7 @@ export default {
|
||||||
class="form-control"
|
class="form-control"
|
||||||
/>
|
/>
|
||||||
</gl-form-group>
|
</gl-form-group>
|
||||||
<gl-form-group class="w-50">
|
<gl-form-group class="w-50" @keydown.enter.prevent.capture>
|
||||||
<label>{{ __('Milestones') }}</label>
|
<label>{{ __('Milestones') }}</label>
|
||||||
<div class="d-flex flex-column col-md-6 col-sm-10 pl-0">
|
<div class="d-flex flex-column col-md-6 col-sm-10 pl-0">
|
||||||
<milestone-combobox
|
<milestone-combobox
|
||||||
|
@ -163,8 +168,6 @@ export default {
|
||||||
data-supports-quick-actions="false"
|
data-supports-quick-actions="false"
|
||||||
:aria-label="__('Release notes')"
|
:aria-label="__('Release notes')"
|
||||||
:placeholder="__('Write your release notes or drag your files here…')"
|
:placeholder="__('Write your release notes or drag your files here…')"
|
||||||
@keydown.meta.enter="saveRelease()"
|
|
||||||
@keydown.ctrl.enter="saveRelease()"
|
|
||||||
></textarea>
|
></textarea>
|
||||||
</template>
|
</template>
|
||||||
</markdown-field>
|
</markdown-field>
|
||||||
|
@ -179,7 +182,7 @@ export default {
|
||||||
category="primary"
|
category="primary"
|
||||||
variant="success"
|
variant="success"
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="isSaveChangesDisabled"
|
:disabled="isFormSubmissionDisabled"
|
||||||
data-testid="submit-button"
|
data-testid="submit-button"
|
||||||
>
|
>
|
||||||
{{ saveButtonLabel }}
|
{{ saveButtonLabel }}
|
||||||
|
|
|
@ -49,6 +49,12 @@ export default {
|
||||||
this.removeAssetLink(linkId);
|
this.removeAssetLink(linkId);
|
||||||
this.ensureAtLeastOneLink();
|
this.ensureAtLeastOneLink();
|
||||||
},
|
},
|
||||||
|
updateUrl(link, newUrl) {
|
||||||
|
this.updateAssetLinkUrl({ linkIdToUpdate: link.id, newUrl });
|
||||||
|
},
|
||||||
|
updateName(link, newName) {
|
||||||
|
this.updateAssetLinkName({ linkIdToUpdate: link.id, newName });
|
||||||
|
},
|
||||||
hasDuplicateUrl(link) {
|
hasDuplicateUrl(link) {
|
||||||
return Boolean(this.getLinkErrors(link).isDuplicate);
|
return Boolean(this.getLinkErrors(link).isDuplicate);
|
||||||
},
|
},
|
||||||
|
@ -138,7 +144,9 @@ export default {
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:state="isUrlValid(link)"
|
:state="isUrlValid(link)"
|
||||||
@change="updateAssetLinkUrl({ linkIdToUpdate: link.id, newUrl: $event })"
|
@change="updateUrl(link, $event)"
|
||||||
|
@keydown.ctrl.enter="updateUrl(link, $event.target.value)"
|
||||||
|
@keydown.meta.enter="updateUrl(link, $event.target.value)"
|
||||||
/>
|
/>
|
||||||
<template #invalid-feedback>
|
<template #invalid-feedback>
|
||||||
<span v-if="hasEmptyUrl(link)" class="invalid-feedback d-inline">
|
<span v-if="hasEmptyUrl(link)" class="invalid-feedback d-inline">
|
||||||
|
@ -175,7 +183,9 @@ export default {
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
:state="isNameValid(link)"
|
:state="isNameValid(link)"
|
||||||
@change="updateAssetLinkName({ linkIdToUpdate: link.id, newName: $event })"
|
@change="updateName(link, $event)"
|
||||||
|
@keydown.ctrl.enter="updateName(link, $event.target.value)"
|
||||||
|
@keydown.meta.enter="updateName(link, $event.target.value)"
|
||||||
/>
|
/>
|
||||||
<template #invalid-feedback>
|
<template #invalid-feedback>
|
||||||
<span v-if="hasEmptyName(link)" class="invalid-feedback d-inline">
|
<span v-if="hasEmptyName(link)" class="invalid-feedback d-inline">
|
||||||
|
|
|
@ -32,5 +32,8 @@ export default {
|
||||||
isMergeImmediatelyDangerous() {
|
isMergeImmediatelyDangerous() {
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
shouldRenderMergeTrainHelperText() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,7 +33,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
||||||
push_frontend_feature_flag(:widget_visibility_polling, @project, default_enabled: true)
|
push_frontend_feature_flag(:widget_visibility_polling, @project, default_enabled: true)
|
||||||
push_frontend_feature_flag(:merge_ref_head_comments, @project, default_enabled: true)
|
push_frontend_feature_flag(:merge_ref_head_comments, @project, default_enabled: true)
|
||||||
push_frontend_feature_flag(:mr_commit_neighbor_nav, @project, default_enabled: true)
|
push_frontend_feature_flag(:mr_commit_neighbor_nav, @project, default_enabled: true)
|
||||||
push_frontend_feature_flag(:multiline_comments, @project)
|
push_frontend_feature_flag(:multiline_comments, @project, default_enabled: true)
|
||||||
push_frontend_feature_flag(:file_identifier_hash)
|
push_frontend_feature_flag(:file_identifier_hash)
|
||||||
push_frontend_feature_flag(:batch_suggestions, @project, default_enabled: true)
|
push_frontend_feature_flag(:batch_suggestions, @project, default_enabled: true)
|
||||||
push_frontend_feature_flag(:auto_expand_collapsed_diffs, @project, default_enabled: true)
|
push_frontend_feature_flag(:auto_expand_collapsed_diffs, @project, default_enabled: true)
|
||||||
|
|
17
app/graphql/resolvers/ci_configuration/sast_resolver.rb
Normal file
17
app/graphql/resolvers/ci_configuration/sast_resolver.rb
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "json"
|
||||||
|
|
||||||
|
module Resolvers
|
||||||
|
module CiConfiguration
|
||||||
|
class SastResolver < BaseResolver
|
||||||
|
SAST_UI_SCHEMA_PATH = 'app/validators/json_schemas/security_ci_configuration_schemas/sast_ui_schema.json'
|
||||||
|
|
||||||
|
type ::Types::CiConfiguration::Sast::Type, null: true
|
||||||
|
|
||||||
|
def resolve(**args)
|
||||||
|
Gitlab::Json.parse(File.read(Rails.root.join(SAST_UI_SCHEMA_PATH)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -175,6 +175,10 @@ module Types
|
||||||
description: 'A single environment of the project',
|
description: 'A single environment of the project',
|
||||||
resolver: Resolvers::EnvironmentsResolver.single
|
resolver: Resolvers::EnvironmentsResolver.single
|
||||||
|
|
||||||
|
field :sast_ci_configuration, ::Types::CiConfiguration::Sast::Type, null: true,
|
||||||
|
description: 'SAST CI configuration for the project',
|
||||||
|
resolver: ::Resolvers::CiConfiguration::SastResolver
|
||||||
|
|
||||||
field :issue,
|
field :issue,
|
||||||
Types::IssueType,
|
Types::IssueType,
|
||||||
null: true,
|
null: true,
|
||||||
|
|
|
@ -4,43 +4,83 @@
|
||||||
"field" : "SECURE_ANALYZERS_PREFIX",
|
"field" : "SECURE_ANALYZERS_PREFIX",
|
||||||
"label" : "Image prefix",
|
"label" : "Image prefix",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default_value": "",
|
"default_value": "registry.gitlab.com/gitlab-org/security-products/analyzers",
|
||||||
"value": "",
|
"value": ""
|
||||||
"description": "Analyzer image's registry prefix (or Name of the registry providing the analyzers' image)"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field" : "SAST_EXCLUDED_PATHS",
|
"field" : "SAST_EXCLUDED_PATHS",
|
||||||
"label" : "Excluded Paths",
|
"label" : "Excluded Paths",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default_value": "",
|
"default_value": "spec, test, tests, tmp",
|
||||||
"value": "",
|
"value": ""
|
||||||
"description": "Comma-separated list of paths to be excluded from analyzer output. Patterns can be globs, file paths, or folder paths."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field" : "SAST_ANALYZER_IMAGE_TAG",
|
"field" : "SAST_ANALYZER_IMAGE_TAG",
|
||||||
"label" : "Image tag",
|
"label" : "Image tag",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default_value": "",
|
"options": [],
|
||||||
"value": "",
|
"default_value": "2",
|
||||||
"description": "Analyzer image's tag"
|
"value": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field" : "SAST_DISABLED",
|
||||||
|
"label" : "Disable SAST",
|
||||||
|
"type": "options",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value" :"true",
|
||||||
|
"label" : "true (disables SAST)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value":"false",
|
||||||
|
"label":"false (enables SAST)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_value": "false",
|
||||||
|
"value": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"pipeline": [
|
"pipeline": [
|
||||||
{
|
{
|
||||||
"field" : "stage",
|
"field" : "stage",
|
||||||
"label" : "Stage",
|
"label" : "Stage",
|
||||||
"type": "string",
|
"type": "dropdown",
|
||||||
"default_value": "",
|
"options": [
|
||||||
"value": "",
|
{
|
||||||
"description": "Pipeline stage in which the scan jobs run"
|
"value" :"test",
|
||||||
|
"label" : "test"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value":"build",
|
||||||
|
"label":"build"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_value": "test",
|
||||||
|
"value": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"field" : "SEARCH_MAX_DEPTH",
|
"field" : "allow_failure",
|
||||||
"label" : "Search maximum depth",
|
"label" : "Allow Failure",
|
||||||
"type": "string",
|
"type": "options",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"value" :"true",
|
||||||
|
"label" : "Allows pipeline failure"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value": "false",
|
||||||
|
"label": "Does not allow pipeline failure"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"default_value": "true",
|
||||||
|
"value": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"field" : "rules",
|
||||||
|
"label" : "Rules",
|
||||||
|
"type": "multiline",
|
||||||
"default_value": "",
|
"default_value": "",
|
||||||
"value": "",
|
"value": ""
|
||||||
"description": "Maximum depth of language and framework detection"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"analyzers": [
|
"analyzers": [
|
||||||
|
|
5
changelogs/unreleased/jdb-mlc-enable-default.yml
Normal file
5
changelogs/unreleased/jdb-mlc-enable-default.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Enable Multiline Comments by default
|
||||||
|
merge_request: 39370
|
||||||
|
author:
|
||||||
|
type: changed
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Improve submission behavior of the New/Edit Release page
|
||||||
|
merge_request: 39145
|
||||||
|
author:
|
||||||
|
type: added
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
# Suggestion: gitlab.Contractions
|
# Suggestion: gitlab.ContractionsDiscard
|
||||||
#
|
#
|
||||||
# Checks for use of common and uncommon contractions.
|
# Suggests a list of agreed-upon contractions to discard.
|
||||||
#
|
#
|
||||||
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
|
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
|
||||||
extends: substitution
|
extends: substitution
|
||||||
|
@ -12,18 +12,6 @@ nonword: false
|
||||||
ignorecase: true
|
ignorecase: true
|
||||||
swap:
|
swap:
|
||||||
|
|
||||||
# Common contractions are ok
|
|
||||||
it is: it's
|
|
||||||
can not: can't
|
|
||||||
cannot: can't
|
|
||||||
do not: don't
|
|
||||||
have not: haven't
|
|
||||||
that is: that's
|
|
||||||
we are: we're
|
|
||||||
would not: wouldn't
|
|
||||||
you are: you're
|
|
||||||
you have: you've
|
|
||||||
|
|
||||||
# Uncommon contractions are not ok
|
# Uncommon contractions are not ok
|
||||||
aren't: are not
|
aren't: are not
|
||||||
couldn't: could not
|
couldn't: could not
|
25
doc/.vale/gitlab/ContractionsKeep.yml
Normal file
25
doc/.vale/gitlab/ContractionsKeep.yml
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
# Suggestion: gitlab.ContractionsKeep
|
||||||
|
#
|
||||||
|
# Suggests a list of agreed-upon contractions to keep.
|
||||||
|
#
|
||||||
|
# For a list of all options, see https://errata-ai.gitbook.io/vale/getting-started/styles
|
||||||
|
extends: substitution
|
||||||
|
message: 'Use "%s" instead of "%s", for a friendly, informal tone.'
|
||||||
|
link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#language
|
||||||
|
level: suggestion
|
||||||
|
nonword: false
|
||||||
|
ignorecase: true
|
||||||
|
swap:
|
||||||
|
|
||||||
|
# Common contractions are ok
|
||||||
|
it is: it's
|
||||||
|
can not: can't
|
||||||
|
cannot: can't
|
||||||
|
do not: don't
|
||||||
|
have not: haven't
|
||||||
|
that is: that's
|
||||||
|
we are: we're
|
||||||
|
would not: wouldn't
|
||||||
|
you are: you're
|
||||||
|
you have: you've
|
|
@ -80,6 +80,7 @@ Please note that the certificate [fingerprint algorithm](#additional-providers-a
|
||||||
With this option enabled, users must go through your group's GitLab single sign-on URL. They may also be added via SCIM, if configured. Users cannot be added manually, and may only access project/group resources via the UI by signing in through the SSO URL.
|
With this option enabled, users must go through your group's GitLab single sign-on URL. They may also be added via SCIM, if configured. Users cannot be added manually, and may only access project/group resources via the UI by signing in through the SSO URL.
|
||||||
|
|
||||||
However, users will not be prompted to sign in through SSO on each visit. GitLab will check whether a user has authenticated through SSO, and will only prompt the user to sign in via SSO if the session has expired.
|
However, users will not be prompted to sign in through SSO on each visit. GitLab will check whether a user has authenticated through SSO, and will only prompt the user to sign in via SSO if the session has expired.
|
||||||
|
You can see more information about how long a session is valid in our [user profile documentation](../../profile/#why-do-i-keep-getting-signed-out).
|
||||||
|
|
||||||
We intend to add a similar SSO requirement for [Git and API activity](https://gitlab.com/gitlab-org/gitlab/-/issues/9152).
|
We intend to add a similar SSO requirement for [Git and API activity](https://gitlab.com/gitlab-org/gitlab/-/issues/9152).
|
||||||
|
|
||||||
|
|
|
@ -151,11 +151,12 @@ in a Merge Request. To do so, click the **{comment}** **comment** icon in the gu
|
||||||
### Commenting on multiple lines
|
### Commenting on multiple lines
|
||||||
|
|
||||||
> - [Introduced](https://gitlab.com/gitlab-org/ux-research/-/issues/870) in GitLab 13.2.
|
> - [Introduced](https://gitlab.com/gitlab-org/ux-research/-/issues/870) in GitLab 13.2.
|
||||||
> - It's deployed behind a feature flag, disabled by default.
|
> - It's deployed behind a feature flag, enabled by default.
|
||||||
|
> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/221268) on GitLab 13.3.
|
||||||
> - It's enabled on GitLab.com.
|
> - It's enabled on GitLab.com.
|
||||||
> - It can be enabled or disabled per-project.
|
> - It can be disabled or enabled per-project.
|
||||||
> - It's recommended for production use.
|
> - It's recommended for production use.
|
||||||
> - For GitLab self-managed instances, GitLab administrators can opt to [enable it](#enable-or-disable-multiline-comments-core-only). **(CORE ONLY)**
|
> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-multiline-comments-core-only). **(CORE ONLY)**
|
||||||
|
|
||||||
GitLab provides a way to select which lines of code a comment refers to. After starting a comment
|
GitLab provides a way to select which lines of code a comment refers to. After starting a comment
|
||||||
a dropdown selector is shown to select the first line that this comment refers to.
|
a dropdown selector is shown to select the first line that this comment refers to.
|
||||||
|
@ -178,18 +179,18 @@ It is deployed behind a feature flag that is **disabled by default**.
|
||||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||||
can opt to enable it for your instance.
|
can opt to enable it for your instance.
|
||||||
|
|
||||||
To enable it:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Feature.enable(:multiline_comments)
|
|
||||||
```
|
|
||||||
|
|
||||||
To disable it:
|
To disable it:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Feature.disable(:multiline_comments)
|
Feature.disable(:multiline_comments)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To enable it:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Feature.enable(:multiline_comments)
|
||||||
|
```
|
||||||
|
|
||||||
## Pipeline status in merge requests widgets
|
## Pipeline status in merge requests widgets
|
||||||
|
|
||||||
If you've set up [GitLab CI/CD](../../../ci/README.md) in your project,
|
If you've set up [GitLab CI/CD](../../../ci/README.md) in your project,
|
||||||
|
|
|
@ -36,7 +36,7 @@ RSpec.describe 'Global search' do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'closes the dropdown on blur', :js do
|
it 'closes the dropdown on blur', :js, quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/201841' do
|
||||||
fill_in 'search', with: "a"
|
fill_in 'search', with: "a"
|
||||||
dropdown = find('.js-dashboard-search-options')
|
dropdown = find('.js-dashboard-search-options')
|
||||||
|
|
||||||
|
|
|
@ -83,11 +83,10 @@ describe('Release edit/new component', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const findSubmitButton = () => wrapper.find('button[type=submit]');
|
const findSubmitButton = () => wrapper.find('button[type=submit]');
|
||||||
|
const findForm = () => wrapper.find('form');
|
||||||
|
|
||||||
describe(`basic functionality tests: all tests unrelated to the "${BACK_URL_PARAM}" parameter`, () => {
|
describe(`basic functionality tests: all tests unrelated to the "${BACK_URL_PARAM}" parameter`, () => {
|
||||||
beforeEach(() => {
|
beforeEach(factory);
|
||||||
factory();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('calls initializeRelease when the component is created', () => {
|
it('calls initializeRelease when the component is created', () => {
|
||||||
expect(actions.initializeRelease).toHaveBeenCalledTimes(1);
|
expect(actions.initializeRelease).toHaveBeenCalledTimes(1);
|
||||||
|
@ -122,15 +121,14 @@ describe('Release edit/new component', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls saveRelease when the form is submitted', () => {
|
it('calls saveRelease when the form is submitted', () => {
|
||||||
wrapper.find('form').trigger('submit');
|
findForm().trigger('submit');
|
||||||
|
|
||||||
expect(actions.saveRelease).toHaveBeenCalledTimes(1);
|
expect(actions.saveRelease).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe(`when the URL does not contain a "${BACK_URL_PARAM}" parameter`, () => {
|
describe(`when the URL does not contain a "${BACK_URL_PARAM}" parameter`, () => {
|
||||||
beforeEach(() => {
|
beforeEach(factory);
|
||||||
factory();
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`renders a "Cancel" button with an href pointing to "${BACK_URL_PARAM}"`, () => {
|
it(`renders a "Cancel" button with an href pointing to "${BACK_URL_PARAM}"`, () => {
|
||||||
const cancelButton = wrapper.find('.js-cancel-button');
|
const cancelButton = wrapper.find('.js-cancel-button');
|
||||||
|
@ -246,6 +244,12 @@ describe('Release edit/new component', () => {
|
||||||
it('renders the submit button as disabled', () => {
|
it('renders the submit button as disabled', () => {
|
||||||
expect(findSubmitButton().attributes('disabled')).toBe('disabled');
|
expect(findSubmitButton().attributes('disabled')).toBe('disabled');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not allow the form to be submitted', () => {
|
||||||
|
findForm().trigger('submit');
|
||||||
|
|
||||||
|
expect(actions.saveRelease).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { mount, createLocalVue } from '@vue/test-utils';
|
||||||
import AssetLinksForm from '~/releases/components/asset_links_form.vue';
|
import AssetLinksForm from '~/releases/components/asset_links_form.vue';
|
||||||
import { release as originalRelease } from '../mock_data';
|
import { release as originalRelease } from '../mock_data';
|
||||||
import * as commonUtils from '~/lib/utils/common_utils';
|
import * as commonUtils from '~/lib/utils/common_utils';
|
||||||
|
import { ENTER_KEY } from '~/lib/utils/keys';
|
||||||
import { ASSET_LINK_TYPE, DEFAULT_ASSET_LINK_TYPE } from '~/releases/constants';
|
import { ASSET_LINK_TYPE, DEFAULT_ASSET_LINK_TYPE } from '~/releases/constants';
|
||||||
|
|
||||||
const localVue = createLocalVue();
|
const localVue = createLocalVue();
|
||||||
|
@ -91,42 +92,128 @@ describe('Release edit component', () => {
|
||||||
expect(actions.removeAssetLink).toHaveBeenCalledTimes(1);
|
expect(actions.removeAssetLink).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls the "updateAssetLinkUrl" store method when text is entered into the "URL" input field', () => {
|
describe('URL input field', () => {
|
||||||
const linkIdToUpdate = release.assets.links[0].id;
|
let input;
|
||||||
const newUrl = 'updated url';
|
let linkIdToUpdate;
|
||||||
|
let newUrl;
|
||||||
|
|
||||||
expect(actions.updateAssetLinkUrl).not.toHaveBeenCalled();
|
beforeEach(() => {
|
||||||
|
input = wrapper.find({ ref: 'urlInput' }).element;
|
||||||
|
linkIdToUpdate = release.assets.links[0].id;
|
||||||
|
newUrl = 'updated url';
|
||||||
|
});
|
||||||
|
|
||||||
wrapper.find({ ref: 'urlInput' }).vm.$emit('change', newUrl);
|
const expectStoreMethodNotToBeCalled = () => {
|
||||||
|
expect(actions.updateAssetLinkUrl).not.toHaveBeenCalled();
|
||||||
|
};
|
||||||
|
|
||||||
expect(actions.updateAssetLinkUrl).toHaveBeenCalledTimes(1);
|
const dispatchKeydowEvent = eventParams => {
|
||||||
expect(actions.updateAssetLinkUrl).toHaveBeenCalledWith(
|
const event = new KeyboardEvent('keydown', eventParams);
|
||||||
expect.anything(),
|
|
||||||
{
|
input.dispatchEvent(event);
|
||||||
linkIdToUpdate,
|
};
|
||||||
newUrl,
|
|
||||||
},
|
const expectStoreMethodToBeCalled = () => {
|
||||||
undefined,
|
expect(actions.updateAssetLinkUrl).toHaveBeenCalledTimes(1);
|
||||||
);
|
expect(actions.updateAssetLinkUrl).toHaveBeenCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
{
|
||||||
|
linkIdToUpdate,
|
||||||
|
newUrl,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('calls the "updateAssetLinkUrl" store method when text is entered into the "URL" input field', () => {
|
||||||
|
expectStoreMethodNotToBeCalled();
|
||||||
|
|
||||||
|
wrapper.find({ ref: 'urlInput' }).vm.$emit('change', newUrl);
|
||||||
|
|
||||||
|
expectStoreMethodToBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls the "updateAssetLinkUrl" store method when Ctrl+Enter is pressed inside the "URL" input field', () => {
|
||||||
|
expectStoreMethodNotToBeCalled();
|
||||||
|
|
||||||
|
input.value = newUrl;
|
||||||
|
|
||||||
|
dispatchKeydowEvent({ key: ENTER_KEY, ctrlKey: true });
|
||||||
|
|
||||||
|
expectStoreMethodToBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls the "updateAssetLinkUrl" store method when Cmd+Enter is pressed inside the "URL" input field', () => {
|
||||||
|
expectStoreMethodNotToBeCalled();
|
||||||
|
|
||||||
|
input.value = newUrl;
|
||||||
|
|
||||||
|
dispatchKeydowEvent({ key: ENTER_KEY, metaKey: true });
|
||||||
|
|
||||||
|
expectStoreMethodToBeCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls the "updateAssetLinkName" store method when text is entered into the "Link title" input field', () => {
|
describe('Link title field', () => {
|
||||||
const linkIdToUpdate = release.assets.links[0].id;
|
let input;
|
||||||
const newName = 'updated name';
|
let linkIdToUpdate;
|
||||||
|
let newName;
|
||||||
|
|
||||||
expect(actions.updateAssetLinkName).not.toHaveBeenCalled();
|
beforeEach(() => {
|
||||||
|
input = wrapper.find({ ref: 'nameInput' }).element;
|
||||||
|
linkIdToUpdate = release.assets.links[0].id;
|
||||||
|
newName = 'updated name';
|
||||||
|
});
|
||||||
|
|
||||||
wrapper.find({ ref: 'nameInput' }).vm.$emit('change', newName);
|
const expectStoreMethodNotToBeCalled = () => {
|
||||||
|
expect(actions.updateAssetLinkUrl).not.toHaveBeenCalled();
|
||||||
|
};
|
||||||
|
|
||||||
expect(actions.updateAssetLinkName).toHaveBeenCalledTimes(1);
|
const dispatchKeydowEvent = eventParams => {
|
||||||
expect(actions.updateAssetLinkName).toHaveBeenCalledWith(
|
const event = new KeyboardEvent('keydown', eventParams);
|
||||||
expect.anything(),
|
|
||||||
{
|
input.dispatchEvent(event);
|
||||||
linkIdToUpdate,
|
};
|
||||||
newName,
|
|
||||||
},
|
const expectStoreMethodToBeCalled = () => {
|
||||||
undefined,
|
expect(actions.updateAssetLinkName).toHaveBeenCalledTimes(1);
|
||||||
);
|
expect(actions.updateAssetLinkName).toHaveBeenCalledWith(
|
||||||
|
expect.anything(),
|
||||||
|
{
|
||||||
|
linkIdToUpdate,
|
||||||
|
newName,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('calls the "updateAssetLinkName" store method when text is entered into the "Link title" input field', () => {
|
||||||
|
expectStoreMethodNotToBeCalled();
|
||||||
|
|
||||||
|
wrapper.find({ ref: 'nameInput' }).vm.$emit('change', newName);
|
||||||
|
|
||||||
|
expectStoreMethodToBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls the "updateAssetLinkName" store method when Ctrl+Enter is pressed inside the "Link title" input field', () => {
|
||||||
|
expectStoreMethodNotToBeCalled();
|
||||||
|
|
||||||
|
input.value = newName;
|
||||||
|
|
||||||
|
dispatchKeydowEvent({ key: ENTER_KEY, ctrlKey: true });
|
||||||
|
|
||||||
|
expectStoreMethodToBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls the "updateAssetLinkName" store method when Cmd+Enter is pressed inside the "Link title" input field', () => {
|
||||||
|
expectStoreMethodNotToBeCalled();
|
||||||
|
|
||||||
|
input.value = newName;
|
||||||
|
|
||||||
|
dispatchKeydowEvent({ key: ENTER_KEY, metaKey: true });
|
||||||
|
|
||||||
|
expectStoreMethodToBeCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls the "updateAssetLinkType" store method when an option is selected from the "Type" dropdown', () => {
|
it('calls the "updateAssetLinkType" store method when an option is selected from the "Type" dropdown', () => {
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
RSpec.describe Resolvers::CiConfiguration::SastResolver do
|
||||||
|
include GraphqlHelpers
|
||||||
|
|
||||||
|
let_it_be(:user) { create(:user) }
|
||||||
|
let_it_be(:project) { create(:project) }
|
||||||
|
|
||||||
|
describe '#resolve' do
|
||||||
|
subject(:sast_config) { resolve(described_class, ctx: { current_user: user }, obj: project) }
|
||||||
|
|
||||||
|
it 'returns global variable informations related to SAST' do
|
||||||
|
expect(sast_config['global'].first['field']).to eql("SECURE_ANALYZERS_PREFIX")
|
||||||
|
expect(sast_config['global'].first['label']).to eql("Image prefix")
|
||||||
|
expect(sast_config['global'].first['type']).to eql("string")
|
||||||
|
|
||||||
|
expect(sast_config['pipeline'].first['field']).to eql("stage")
|
||||||
|
expect(sast_config['pipeline'].first['label']).to eql("Stage")
|
||||||
|
expect(sast_config['pipeline'].first['type']).to eql("dropdown")
|
||||||
|
|
||||||
|
expect(sast_config['analyzers'].first['name']).to eql("brakeman")
|
||||||
|
expect(sast_config['analyzers'].first['label']).to eql("Brakeman")
|
||||||
|
expect(sast_config['analyzers'].first['enabled']).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -26,7 +26,7 @@ RSpec.describe GitlabSchema.types['Project'] do
|
||||||
grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments
|
grafanaIntegration autocloseReferencedIssues suggestion_commit_message environments
|
||||||
environment boards jira_import_status jira_imports services releases release
|
environment boards jira_import_status jira_imports services releases release
|
||||||
alert_management_alerts alert_management_alert alert_management_alert_status_counts
|
alert_management_alerts alert_management_alert alert_management_alert_status_counts
|
||||||
container_expiration_policy service_desk_enabled service_desk_address
|
container_expiration_policy sast_ci_configuration service_desk_enabled service_desk_address
|
||||||
issue_status_counts
|
issue_status_counts
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -150,5 +150,93 @@ RSpec.describe GitlabSchema.types['Project'] do
|
||||||
it { is_expected.to have_graphql_type(Types::ContainerExpirationPolicyType) }
|
it { is_expected.to have_graphql_type(Types::ContainerExpirationPolicyType) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'sast_ci_configuration' do
|
||||||
|
let_it_be(:project) { create(:project) }
|
||||||
|
let_it_be(:user) { create(:user) }
|
||||||
|
let_it_be(:query) do
|
||||||
|
%(
|
||||||
|
query {
|
||||||
|
project(fullPath: "#{project.full_path}") {
|
||||||
|
sastCiConfiguration {
|
||||||
|
global {
|
||||||
|
nodes {
|
||||||
|
type
|
||||||
|
options {
|
||||||
|
nodes {
|
||||||
|
label
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field
|
||||||
|
label
|
||||||
|
defaultValue
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pipeline {
|
||||||
|
nodes {
|
||||||
|
type
|
||||||
|
options {
|
||||||
|
nodes {
|
||||||
|
label
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field
|
||||||
|
label
|
||||||
|
defaultValue
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
analyzers {
|
||||||
|
nodes {
|
||||||
|
name
|
||||||
|
label
|
||||||
|
enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_developer(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the project's sast configuration for global variables" do
|
||||||
|
query_result = subject.dig('data', 'project', 'sastCiConfiguration', 'global', 'nodes')
|
||||||
|
first_config = query_result.first
|
||||||
|
fourth_config = query_result[3]
|
||||||
|
expect(first_config['type']).to eq('string')
|
||||||
|
expect(first_config['field']).to eq('SECURE_ANALYZERS_PREFIX')
|
||||||
|
expect(first_config['label']).to eq('Image prefix')
|
||||||
|
expect(first_config['defaultValue']).to eq('registry.gitlab.com/gitlab-org/security-products/analyzers')
|
||||||
|
expect(first_config['value']).to eq('')
|
||||||
|
expect(first_config['options']).to be_nil
|
||||||
|
expect(fourth_config['options']['nodes']).to match([{ "value" => "true", "label" => "true (disables SAST)" },
|
||||||
|
{ "value" => "false", "label" => "false (enables SAST)" }])
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the project's sast configuration for pipeline variables" do
|
||||||
|
configuration = subject.dig('data', 'project', 'sastCiConfiguration', 'pipeline', 'nodes').first
|
||||||
|
expect(configuration['type']).to eq('dropdown')
|
||||||
|
expect(configuration['field']).to eq('stage')
|
||||||
|
expect(configuration['label']).to eq('Stage')
|
||||||
|
expect(configuration['defaultValue']).to eq('test')
|
||||||
|
expect(configuration['value']).to eq('')
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns the project's sast configuration for analyzer variables" do
|
||||||
|
configuration = subject.dig('data', 'project', 'sastCiConfiguration', 'analyzers', 'nodes').first
|
||||||
|
expect(configuration['name']).to eq('brakeman')
|
||||||
|
expect(configuration['label']).to eq('Brakeman')
|
||||||
|
expect(configuration['enabled']).to eq(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it_behaves_like 'a GraphQL type with labels'
|
it_behaves_like 'a GraphQL type with labels'
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
include:
|
|
||||||
- template: SAST.gitlab-ci.yml
|
|
||||||
|
|
||||||
variables:
|
|
||||||
SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers2"
|
|
||||||
SAST_EXCLUDED_PATHS: "spec, executables"
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- our_custom_security_stage
|
|
||||||
sast:
|
|
||||||
stage: our_custom_security_stage
|
|
||||||
variables:
|
|
||||||
SEARCH_MAX_DEPTH: 8
|
|
|
@ -1,9 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
RSpec.shared_context 'read ci configuration for sast enabled project' do
|
|
||||||
let_it_be(:gitlab_ci_yml_content) do
|
|
||||||
File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci_for_sast.yml'))
|
|
||||||
end
|
|
||||||
|
|
||||||
let_it_be(:project) { create(:project, :repository) }
|
|
||||||
end
|
|
Loading…
Reference in a new issue