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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue40
-rw-r--r--app/assets/javascripts/merge_requests/components/compare_app.vue31
-rw-r--r--app/graphql/types/ci/code_quality_report_summary_type.rb20
-rw-r--r--app/models/release.rb3
-rw-r--r--doc/administration/raketasks/project_import_export.md2
-rw-r--r--doc/api/graphql/reference/index.md17
-rw-r--r--doc/development/import_export.md5
-rw-r--r--doc/integration/jenkins.md35
-rw-r--r--doc/user/group/import/index.md40
-rw-r--r--doc/user/profile/account/delete_account.md1
-rw-r--r--doc/user/project/settings/import_export.md54
-rw-r--r--lib/gitlab/ci/reports/codequality_reports.rb12
-rw-r--r--spec/features/triggers_spec.rb15
-rw-r--r--spec/frontend/__helpers__/init_vue_mr_page_helper.js3
-rw-r--r--spec/frontend/blob/notebook/notebook_viever_spec.js6
-rw-r--r--spec/frontend/branches/divergence_graph_spec.js3
-rw-r--r--spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js94
-rw-r--r--spec/frontend/commits_spec.js3
-rw-r--r--spec/frontend/merge_request_spec.js4
-rw-r--r--spec/frontend/merge_requests/components/compare_app_spec.js50
-rw-r--r--spec/frontend/pager_spec.js3
-rw-r--r--spec/frontend/pipelines/pipelines_spec.js46
-rw-r--r--spec/frontend/pipelines/test_reports/stores/actions_spec.js5
-rw-r--r--spec/frontend/projects/commits/store/actions_spec.js4
-rw-r--r--spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js6
-rw-r--r--spec/frontend/projects/compare/components/revision_dropdown_spec.js4
-rw-r--r--spec/frontend/protected_branches/protected_branch_edit_spec.js6
-rw-r--r--spec/frontend/search_autocomplete_spec.js3
-rw-r--r--spec/frontend/sidebar/components/labels/labels_select_vue/store/actions_spec.js6
-rw-r--r--spec/frontend/vue_merge_request_widget/stores/artifacts_list/actions_spec.js12
-rw-r--r--spec/frontend/vue_shared/alert_details/sidebar/alert_sidebar_assignees_spec.js5
-rw-r--r--spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js10
-rw-r--r--spec/frontend/vue_shared/security_reports/security_reports_app_spec.js6
-rw-r--r--spec/frontend/vue_shared/security_reports/store/modules/sast/actions_spec.js12
-rw-r--r--spec/frontend/vue_shared/security_reports/store/modules/secret_detection/actions_spec.js12
-rw-r--r--spec/frontend/whats_new/store/actions_spec.js7
-rw-r--r--spec/graphql/types/ci/pipeline_type_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/reports/codequality_reports_spec.rb115
-rw-r--r--spec/models/release_spec.rb7
40 files changed, 457 insertions, 254 deletions
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 89a5c6b44c3..bd3e8d510ee 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-9e9717d60e83bc08e6bcb3ca0e636edf1668a9b3
+988e9e17374a0ae05a155ce0b459145c389c1524
diff --git a/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue b/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue
index 1f8096da94d..a1b264cfe54 100644
--- a/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue
+++ b/app/assets/javascripts/ci_settings_pipeline_triggers/components/triggers_list.vue
@@ -8,7 +8,8 @@ import {
GlTable,
GlTooltipDirective,
} from '@gitlab/ui';
-import { s__ } from '~/locale';
+import { __, s__ } from '~/locale';
+import { thWidthPercent } from '~/lib/utils/table_utility';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
@@ -43,43 +44,70 @@ export default {
default: () => [],
},
},
+ data() {
+ return {
+ areValuesHidden: true,
+ };
+ },
fields: [
{
key: 'token',
label: s__('Pipelines|Token'),
+ thClass: thWidthPercent(70),
},
{
key: 'description',
label: s__('Pipelines|Description'),
+ thClass: thWidthPercent(15),
},
{
key: 'owner',
label: s__('Pipelines|Owner'),
+ thClass: thWidthPercent(5),
},
{
key: 'lastUsed',
label: s__('Pipelines|Last Used'),
+ thClass: thWidthPercent(5),
},
{
key: 'actions',
label: '',
tdClass: 'gl-text-right gl-white-space-nowrap',
+ thClass: thWidthPercent(5),
},
],
+ computed: {
+ valuesButtonText() {
+ return this.areValuesHidden ? __('Reveal values') : __('Hide values');
+ },
+ hasTriggers() {
+ return this.triggers.length;
+ },
+ maskedToken() {
+ return '*'.repeat(47);
+ },
+ },
+ methods: {
+ toggleHiddenState() {
+ this.areValuesHidden = !this.areValuesHidden;
+ },
+ },
};
</script>
<template>
<div>
<gl-table
- v-if="triggers.length"
+ v-if="hasTriggers"
:fields="$options.fields"
:items="triggers"
class="triggers-list"
responsive
>
<template #cell(token)="{ item }">
- {{ item.token }}
+ <span v-if="!areValuesHidden">{{ item.token }}</span>
+ <span v-else>{{ maskedToken }}</span>
<clipboard-button
v-if="item.hasTokenExposed"
:text="item.token"
@@ -157,5 +185,11 @@ export default {
>
{{ s__('Pipelines|No triggers have been created yet. Add one using the form above.') }}
</gl-alert>
+ <gl-button
+ v-if="hasTriggers"
+ data-testid="reveal-hide-values-button"
+ @click="toggleHiddenState"
+ >{{ valuesButtonText }}</gl-button
+ >
</div>
</template>
diff --git a/app/assets/javascripts/merge_requests/components/compare_app.vue b/app/assets/javascripts/merge_requests/components/compare_app.vue
index 4973f8596b0..0e60f4bd8f9 100644
--- a/app/assets/javascripts/merge_requests/components/compare_app.vue
+++ b/app/assets/javascripts/merge_requests/components/compare_app.vue
@@ -21,7 +21,7 @@ export default {
default: '',
},
currentProject: {
- default: '',
+ default: () => ({}),
},
currentBranch: {
default: () => ({}),
@@ -50,6 +50,9 @@ export default {
return [this.currentProject];
},
+ showCommitBox() {
+ return this.commitHtml || this.loading || !this.selectedBranch.value;
+ },
},
mounted() {
this.fetchCommit();
@@ -63,6 +66,8 @@ export default {
this.fetchCommit();
},
async fetchCommit() {
+ if (!this.selectedBranch.value) return;
+
this.loading = true;
const { data } = await axios.get(this.branchCommitPath, {
@@ -104,16 +109,22 @@ export default {
/>
</div>
</div>
- <div v-if="commitHtml || loading" class="gl-bg-gray-50 gl-rounded-base gl-my-4">
+ <div
+ v-if="showCommitBox"
+ class="gl-bg-gray-50 gl-rounded-base gl-my-4"
+ data-testid="commit-box"
+ >
<gl-loading-icon v-if="loading" class="gl-py-3" />
- <div
- v-if="!selectedBranch.value"
- class="compare-commit-empty gl-display-flex gl-align-items-center gl-p-5"
- >
- <gl-icon name="branch" class="gl-mr-3" />
- {{ __('Select a branch to compare') }}
- </div>
- <ul v-safe-html="commitHtml" class="list-unstyled mr_source_commit"></ul>
+ <template v-else>
+ <div
+ v-if="!selectedBranch.value"
+ class="compare-commit-empty gl-display-flex gl-align-items-center gl-p-5"
+ >
+ <gl-icon name="branch" class="gl-mr-3" />
+ {{ __('Select a branch to compare') }}
+ </div>
+ <ul v-safe-html="commitHtml" class="list-unstyled mr_source_commit"></ul>
+ </template>
</div>
</div>
</template>
diff --git a/app/graphql/types/ci/code_quality_report_summary_type.rb b/app/graphql/types/ci/code_quality_report_summary_type.rb
new file mode 100644
index 00000000000..0d560d9e9e8
--- /dev/null
+++ b/app/graphql/types/ci/code_quality_report_summary_type.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Types
+ module Ci
+ # rubocop: disable Graphql/AuthorizeTypes
+ # This is presented through `PipelineType` that has its own authorization
+ class CodeQualityReportSummaryType < BaseObject
+ graphql_name 'CodeQualityReportSummary'
+ description 'Code Quality report for a pipeline'
+
+ field :count, GraphQL::Types::Int, null: true,
+ description: 'Total number of Code Quality reports.'
+ ::Gitlab::Ci::Reports::CodequalityReports::SEVERITY_PRIORITIES.each_key do |status|
+ field status, GraphQL::Types::Int, null: true,
+ description: "Total number of #{status} status."
+ end
+ end
+ # rubocop: enable Graphql/AuthorizeTypes
+ end
+end
diff --git a/app/models/release.rb b/app/models/release.rb
index b770f3934ef..85699d259f5 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -11,7 +11,6 @@ class Release < ApplicationRecord
cache_markdown_field :description
belongs_to :project, touch: true
- # releases prior to 11.7 have no author
belongs_to :author, class_name: 'User'
has_many :links, class_name: 'Releases::Link'
@@ -26,7 +25,7 @@ class Release < ApplicationRecord
before_create :set_released_at
validates :project, :tag, presence: true
- validates :author_id, presence: true, if: :validate_release_with_author?
+ validates :author_id, presence: true, on: :create, if: :validate_release_with_author?
validates :tag, uniqueness: { scope: :project_id }
diff --git a/doc/administration/raketasks/project_import_export.md b/doc/administration/raketasks/project_import_export.md
index e43fbac25e9..4694af18af2 100644
--- a/doc/administration/raketasks/project_import_export.md
+++ b/doc/administration/raketasks/project_import_export.md
@@ -42,7 +42,7 @@ bundle exec rake gitlab:import_export:data RAILS_ENV=production
Note the following:
- Importing is only possible if the version of the import and export GitLab instances are
- compatible as described in the [Version history](../../user/project/settings/import_export.md#version-history).
+ [compatible](../../user/project/settings/import_export.md#compatibility).
- The project import option must be enabled:
1. On the top bar, select **Main menu > Admin**.
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index ff668d7d66b..26765eac2a6 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -11617,6 +11617,22 @@ Represents a code quality degradation on the pipeline.
| <a id="codequalitydegradationseverity"></a>`severity` | [`CodeQualityDegradationSeverity!`](#codequalitydegradationseverity) | Status of the degradation (BLOCKER, CRITICAL, MAJOR, MINOR, INFO, UNKNOWN). |
| <a id="codequalitydegradationweburl"></a>`webUrl` | [`String`](#string) | URL to the file along with line number. |
+### `CodeQualityReportSummary`
+
+Code Quality report for a pipeline.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="codequalityreportsummaryblocker"></a>`blocker` | [`Int`](#int) | Total number of blocker status. |
+| <a id="codequalityreportsummarycount"></a>`count` | [`Int`](#int) | Total number of Code Quality reports. |
+| <a id="codequalityreportsummarycritical"></a>`critical` | [`Int`](#int) | Total number of critical status. |
+| <a id="codequalityreportsummaryinfo"></a>`info` | [`Int`](#int) | Total number of info status. |
+| <a id="codequalityreportsummarymajor"></a>`major` | [`Int`](#int) | Total number of major status. |
+| <a id="codequalityreportsummaryminor"></a>`minor` | [`Int`](#int) | Total number of minor status. |
+| <a id="codequalityreportsummaryunknown"></a>`unknown` | [`Int`](#int) | Total number of unknown status. |
+
### `Commit`
#### Fields
@@ -17104,6 +17120,7 @@ Represents a file or directory in the project repository that has been locked.
| <a id="pipelineactive"></a>`active` | [`Boolean!`](#boolean) | Indicates if the pipeline is active. |
| <a id="pipelinebeforesha"></a>`beforeSha` | [`String`](#string) | Base SHA of the source branch. |
| <a id="pipelinecancelable"></a>`cancelable` | [`Boolean!`](#boolean) | Specifies if a pipeline can be canceled. |
+| <a id="pipelinecodequalityreportsummary"></a>`codeQualityReportSummary` | [`CodeQualityReportSummary`](#codequalityreportsummary) | Code Quality report summary for a pipeline. |
| <a id="pipelinecodequalityreports"></a>`codeQualityReports` | [`CodeQualityDegradationConnection`](#codequalitydegradationconnection) | Code Quality degradations reported on the pipeline. (see [Connections](#connections)) |
| <a id="pipelinecommit"></a>`commit` | [`Commit`](#commit) | Git commit of the pipeline. |
| <a id="pipelinecommitpath"></a>`commitPath` | [`String`](#string) | Path to the commit that triggered the pipeline. |
diff --git a/doc/development/import_export.md b/doc/development/import_export.md
index 9aab7f38dbb..17e733e8904 100644
--- a/doc/development/import_export.md
+++ b/doc/development/import_export.md
@@ -105,10 +105,9 @@ module Gitlab
VERSION = '0.2.4'
```
-## Version history
+## Compatibility
-Check the [version history](../user/project/settings/import_export.md#version-history)
-for compatibility when importing and exporting projects.
+Check for [compatibility](../user/project/settings/import_export.md#compatibility) when importing and exporting projects.
### When to bump the version up
diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md
index 53a2fb8bbdd..6950ad38101 100644
--- a/doc/integration/jenkins.md
+++ b/doc/integration/jenkins.md
@@ -33,31 +33,20 @@ The Jenkins integration requires configuration in both GitLab and Jenkins.
## Grant Jenkins access to the GitLab project
-Grant a GitLab user access to the relevant GitLab projects.
+To grant Jenkins access to the GitLab project:
-1. Create a new GitLab user, or choose an existing GitLab user.
+1. Create a personal, project, or group access token.
- This account is used by Jenkins to access the GitLab projects. We recommend creating a GitLab
- user for only this purpose. If you use a person's account, and their account is deactivated or
- deleted, the Jenkins integration stops working.
+ - [Create a personal access token](../user/profile/personal_access_tokens.md#create-a-personal-access-token)
+ to use the token for all Jenkins integrations of that user.
+ - [Create a project access token](../user/project/settings/project_access_tokens.md#create-a-project-access-token)
+ to use the token at the project level only. For instance, you can revoke
+ the token in a project without affecting Jenkins integrations in other projects.
+ - [Create a group access token](../user/group/settings/group_access_tokens.md#create-a-group-access-token-using-ui)
+ to use the token for all Jenkins integrations in all projects of that group.
-1. Grant the user permission to the GitLab projects.
-
- If you're integrating Jenkins with many GitLab projects, consider granting the
- user administrator access. Otherwise, add the user to each project
- and grant the Maintainer role.
-
-## Grant Jenkins access to the GitLab API
-
-Create a personal access token to authorize Jenkins to access GitLab.
-
-1. Sign in to GitLab as the user to be used with Jenkins.
-1. On the top bar, in the top right corner, select your avatar.
-1. Select **Edit profile**.
-1. On the left sidebar, select **Access Tokens**.
-1. Create a [personal access token](../user/profile/personal_access_tokens.md) and
- select the **API** scope.
-1. Copy the personal access token. You need it to [configure the Jenkins server](#configure-the-jenkins-server).
+1. Set the access token scope to **API**.
+1. Copy the access token value to [configure the Jenkins server](#configure-the-jenkins-server).
## Configure the Jenkins server
@@ -70,7 +59,7 @@ authorize the connection to GitLab.
1. In the **GitLab** section, select **Enable authentication for '/project' end-point**.
1. Select **Add**, then choose **Jenkins Credential Provider**.
1. Select **GitLab API token** as the token type.
-1. Enter the GitLab personal access token's value in **API Token** and select **Add**.
+1. In **API Token**, [paste the value you copied from GitLab](#grant-jenkins-access-to-the-gitlab-project) and select **Add**.
1. Enter the GitLab server's URL in **GitLab host URL**.
1. To test the connection, select **Test Connection**.
diff --git a/doc/user/group/import/index.md b/doc/user/group/import/index.md
index 02d4f098b5d..a56860118a1 100644
--- a/doc/user/group/import/index.md
+++ b/doc/user/group/import/index.md
@@ -342,6 +342,25 @@ Note the following:
exporting a group from the Enterprise Edition to the Community Edition, you may lose this data. For more information,
see [downgrading from EE to CE](../../../index.md).
+### Compatibility
+
+Group file exports are in NDJSON format. GitLab previously produced group file exports in JSON format, however:
+
+- From GitLab 15.8, GitLab no longer supports importing a JSON-formatted group file export.
+- Between GitLab 14.0 and GitLab 14.7, GitLab no longer produces group file exports in JSON format but, to support
+ transitions, can still import JSON-formatted group file exports.
+
+From GitLab 13.0, GitLab can import group file exports that were exported from a version of GitLab up to two
+[minor](../../../policy/maintenance.md#versioning) versions behind, which is similar to our process for
+[security releases](../../../policy/maintenance.md#security-releases).
+
+For example:
+
+| Destination version | Compatible source versions |
+|:--------------------|:---------------------------|
+| 13.0 | 13.0, 12.10, 12.9 |
+| 13.1 | 13.1, 13.0, 12.10 |
+
### Exported contents
The [`import_export.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/import_export/group/import_export.yml)
@@ -440,27 +459,6 @@ To help avoid abuse, by default, users are rate limited to:
| Download export | 1 download per group per minute |
| Import | 6 groups per minute |
-### Version history
-
-#### 14.0+
-
-In GitLab 14.0, the JSON format is no longer supported for project and group exports. To allow for a
-transitional period, you can still import any JSON exports. The new format for imports and exports
-is NDJSON.
-
-#### 13.0+
-
-GitLab can import bundles that were exported from a different GitLab deployment.
-This ability is limited to two previous GitLab [minor](../../../policy/maintenance.md#versioning)
-releases, which is similar to our process for [Security Releases](../../../policy/maintenance.md#security-releases).
-
-For example:
-
-| Current version | Can import bundles exported from |
-|-----------------|----------------------------------|
-| 13.0 | 13.0, 12.10, 12.9 |
-| 13.1 | 13.1, 13.0, 12.10 |
-
## Automate group and project import **(PREMIUM)**
For information on automating user, group, and project import API calls, see
diff --git a/doc/user/profile/account/delete_account.md b/doc/user/profile/account/delete_account.md
index 18b4e53fb31..5e2908a26e1 100644
--- a/doc/user/profile/account/delete_account.md
+++ b/doc/user/profile/account/delete_account.md
@@ -56,7 +56,6 @@ When deleting users, you can either:
- Issues.
- Merge requests.
- Notes and comments.
- - Releases.
- Personal access tokens.
- Snippets.
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 14068a1093a..2b5f5dfa21d 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -27,6 +27,30 @@ To preserve contribution history, [migrate using direct transfer](../../group/im
If you migrate from GitLab.com to self-managed GitLab, an administrator can create users on the self-managed GitLab instance.
+## Compatibility
+
+FLAG:
+On self-managed GitLab by default project, file exports are in NDJSON format. To make GitLab produce project file
+exports in JSON format, ask an administrator to [disable the feature flags](../../../administration/feature_flags.md)
+named `project_export_as_ndjson`. To allow GitLab to import project file exports in JSON format, ask an administrator to
+[disable the feature flags](../../../administration/feature_flags.md) named `project_import_ndjson`. On GitLab.com,
+project file exports are in NDJSON format only.
+
+Project file exports are in NDJSON format. Before version 14.0, GitLab produced project file exports in JSON format.
+To support transitions, you can still import JSON-formatted project file exports if you configure the relevant feature
+flags.
+
+From GitLab 13.0, GitLab can import project file exports that were exported from a version of GitLab up to two
+[minor](../../../policy/maintenance.md#versioning) versions behind, which is similar to our process for
+[security releases](../../../policy/maintenance.md#security-releases).
+
+For example:
+
+| Destination version | Compatible source versions |
+|:--------------------|:---------------------------|
+| 13.0 | 13.0, 12.10, 12.9 |
+| 13.1 | 13.1, 13.0, 12.10 |
+
## Configure file exports as an import source **(FREE SELF)**
Before you can migrate projects on a self-managed GitLab instance using file exports, GitLab administrators must:
@@ -47,7 +71,7 @@ To enable file exports as an import source for the destination instance:
## Between CE and EE
You can export projects from the [Community Edition to the Enterprise Edition](https://about.gitlab.com/install/ce-or-ee/)
-and vice versa. This assumes [version history](#version-history) requirements are met.
+and vice versa, assuming [compatibility](#compatibility) is met.
If you're exporting a project from the Enterprise Edition to the Community Edition, you may lose
data that is retained only in the Enterprise Edition. For more information, see
@@ -146,7 +170,7 @@ Prerequisites:
- You must have [exported the project and its data](#export-a-project-and-its-data).
- Compare GitLab versions and ensure you are importing to a GitLab version that is the same or later
than the GitLab version you exported to.
-- Review the [Version history](#version-history) for compatibility issues.
+- Review [compatibility](#compatibility) for any issues.
- At least the Maintainer role on the destination group to migrate to. Using the Developer role for this purpose was
[deprecated](https://gitlab.com/gitlab-org/gitlab/-/issues/387891) in GitLab 15.8 and will be removed in GitLab 16.0.
@@ -221,32 +245,6 @@ To help avoid abuse, by default, users are rate limited to:
| Download export | 1 download per group per minute |
| Import | 6 projects per minute |
-## Version history
-
-### 15.8+
-
-Starting with GitLab 15.8, importing groups from a JSON export is no longer supported. Groups must be imported
-in NDJSON format.
-
-### 14.0+
-
-In GitLab 14.0, the JSON format is no longer supported for project and group exports. To allow for a
-transitional period, you can still import any JSON exports. The new format for imports and exports
-is NDJSON.
-
-### 13.0+
-
-Starting with GitLab 13.0, GitLab can import bundles that were exported from a different GitLab deployment.
-**This ability is limited to two previous GitLab [minor](../../../policy/maintenance.md#versioning)
-releases**, which is similar to our process for [Security Releases](../../../policy/maintenance.md#security-releases).
-
-For example:
-
-| Current version | Can import bundles exported from |
-|-----------------|----------------------------------|
-| 13.0 | 13.0, 12.10, 12.9 |
-| 13.1 | 13.1, 13.0, 12.10 |
-
## Related topics
- [Project import and export API](../../../api/project_import_export.md)
diff --git a/lib/gitlab/ci/reports/codequality_reports.rb b/lib/gitlab/ci/reports/codequality_reports.rb
index 3196bf3fc6d..aba2d2e8b19 100644
--- a/lib/gitlab/ci/reports/codequality_reports.rb
+++ b/lib/gitlab/ci/reports/codequality_reports.rb
@@ -42,6 +42,18 @@ module Gitlab
rescue StandardError => _
false
end
+
+ def code_quality_report_summary
+ report_degradations = @degradations.presence
+ return if report_degradations.nil?
+
+ summary = ::Gitlab::Ci::Reports::CodequalityReports::SEVERITY_PRIORITIES.keys.index_with(0)
+ report_degradations.each_value do |degradation|
+ summary[degradation[:severity]] += 1
+ end
+ summary['count'] = summary.values.sum
+ summary
+ end
end
end
end
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index 23a13994fa4..903211ec250 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -113,11 +113,24 @@ RSpec.describe 'Triggers', :js, feature_category: :continuous_integration do
end
end
+ it 'hides the token value and reveals when clicking the "reveal values" button', :aggregate_failures do
+ create(:ci_trigger, owner: user, project: @project, description: trigger_title)
+ visit project_settings_ci_cd_path(@project)
+
+ expect(page.find('.triggers-list')).to have_content('*' * 47)
+
+ page.find('[data-testid="reveal-hide-values-button"]').click
+
+ expect(page.find('.triggers-list')).to have_content(@project.triggers.first.token)
+ end
+
it 'do not show "Edit" or full token for not owned trigger' do
# Create trigger with user different from current_user
create(:ci_trigger, owner: user2, project: @project, description: trigger_title)
visit project_settings_ci_cd_path(@project)
+ page.find('[data-testid="reveal-hide-values-button"]').click
+
aggregate_failures 'shows truncated token, no clipboard button and no edit link' do
expect(page.find('.triggers-list')).to have_content(@project.triggers.first.token[0..3])
expect(page.find('.triggers-list')).not_to have_selector('[data-testid="clipboard-btn"]')
@@ -130,6 +143,8 @@ RSpec.describe 'Triggers', :js, feature_category: :continuous_integration do
create(:ci_trigger, owner: user, project: @project, description: trigger_title)
visit project_settings_ci_cd_path(@project)
+ page.find('[data-testid="reveal-hide-values-button"]').click
+
aggregate_failures 'shows full token, clipboard button and edit link' do
expect(page.find('.triggers-list')).to have_content @project.triggers.first.token
expect(page.find('.triggers-list')).to have_selector('[data-testid="clipboard-btn"]')
diff --git a/spec/frontend/__helpers__/init_vue_mr_page_helper.js b/spec/frontend/__helpers__/init_vue_mr_page_helper.js
index 83ed0a869dc..d01affdaeac 100644
--- a/spec/frontend/__helpers__/init_vue_mr_page_helper.js
+++ b/spec/frontend/__helpers__/init_vue_mr_page_helper.js
@@ -1,5 +1,6 @@
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import initMRPage from '~/mr_notes';
import { getDiffFileMock } from '../diffs/mock_data/diff_file';
import { userDataMock, notesDataMock, noteableDataMock } from '../notes/mock_data';
@@ -37,7 +38,7 @@ export default function initVueMRPage() {
mrTestEl.appendChild(diffsAppEl);
const mock = new MockAdapter(axios);
- mock.onGet(diffsAppEndpoint).reply(200, {
+ mock.onGet(diffsAppEndpoint).reply(HTTP_STATUS_OK, {
branch_name: 'foo',
diff_files: [getDiffFileMock()],
});
diff --git a/spec/frontend/blob/notebook/notebook_viever_spec.js b/spec/frontend/blob/notebook/notebook_viever_spec.js
index e6480c8d874..2e7eadc912d 100644
--- a/spec/frontend/blob/notebook/notebook_viever_spec.js
+++ b/spec/frontend/blob/notebook/notebook_viever_spec.js
@@ -4,7 +4,7 @@ import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
import component from '~/blob/notebook/notebook_viewer.vue';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
+import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import NotebookLab from '~/notebook/index.vue';
describe('iPython notebook renderer', () => {
@@ -55,7 +55,7 @@ describe('iPython notebook renderer', () => {
describe('successful response', () => {
beforeEach(() => {
- mock.onGet(endpoint).reply(200, mockNotebook);
+ mock.onGet(endpoint).reply(HTTP_STATUS_OK, mockNotebook);
mountComponent();
return waitForPromises();
});
@@ -73,7 +73,7 @@ describe('iPython notebook renderer', () => {
beforeEach(() => {
mock.onGet(endpoint).reply(() =>
// eslint-disable-next-line prefer-promise-reject-errors
- Promise.reject({ status: 200 }),
+ Promise.reject({ status: HTTP_STATUS_OK }),
);
mountComponent();
diff --git a/spec/frontend/branches/divergence_graph_spec.js b/spec/frontend/branches/divergence_graph_spec.js
index 7c367f83add..c3a0f6436c5 100644
--- a/spec/frontend/branches/divergence_graph_spec.js
+++ b/spec/frontend/branches/divergence_graph_spec.js
@@ -1,6 +1,7 @@
import MockAdapter from 'axios-mock-adapter';
import init from '~/branches/divergence_graph';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
describe('Divergence graph', () => {
let mock;
@@ -8,7 +9,7 @@ describe('Divergence graph', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
- mock.onGet('/-/diverging_counts').reply(200, {
+ mock.onGet('/-/diverging_counts').reply(HTTP_STATUS_OK, {
main: { ahead: 1, behind: 1 },
'test/hello-world': { ahead: 1, behind: 1 },
});
diff --git a/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js b/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js
index 01eb08f4ece..f1df4208fa2 100644
--- a/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js
+++ b/spec/frontend/ci_settings_pipeline_triggers/components/triggers_list_spec.js
@@ -1,6 +1,6 @@
import { GlTable, GlBadge } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import { mountExtended } from 'helpers/vue_test_utils_helper';
import TriggersList from '~/ci_settings_pipeline_triggers/components/triggers_list.vue';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
@@ -11,7 +11,7 @@ describe('TriggersList', () => {
let wrapper;
const createComponent = (props = {}) => {
- wrapper = mount(TriggersList, {
+ wrapper = mountExtended(TriggersList, {
propsData: { triggers, ...props },
});
};
@@ -25,59 +25,75 @@ describe('TriggersList', () => {
const findInvalidBadge = (i) => findCell(i, 0).findComponent(GlBadge);
const findEditBtn = (i) => findRowAt(i).find('[data-testid="edit-btn"]');
const findRevokeBtn = (i) => findRowAt(i).find('[data-testid="trigger_revoke_button"]');
+ const findRevealHideButton = () => wrapper.findByTestId('reveal-hide-values-button');
- beforeEach(async () => {
- createComponent();
+ describe('With triggers set', () => {
+ beforeEach(async () => {
+ createComponent();
- await nextTick();
- });
+ await nextTick();
+ });
- it('displays a table with expected headers', () => {
- const headers = ['Token', 'Description', 'Owner', 'Last Used', ''];
- headers.forEach((header, i) => {
- expect(findHeaderAt(i).text()).toBe(header);
+ it('displays a table with expected headers', () => {
+ const headers = ['Token', 'Description', 'Owner', 'Last Used', ''];
+ headers.forEach((header, i) => {
+ expect(findHeaderAt(i).text()).toBe(header);
+ });
});
- });
- it('displays a table with rows', () => {
- expect(findRows()).toHaveLength(triggers.length);
+ it('displays a "Reveal/Hide values" button', async () => {
+ const revealHideButton = findRevealHideButton();
- const [trigger] = triggers;
+ expect(revealHideButton.exists()).toBe(true);
+ expect(revealHideButton.text()).toBe('Reveal values');
- expect(findCell(0, 0).text()).toBe(trigger.token);
- expect(findCell(0, 1).text()).toBe(trigger.description);
- expect(findCell(0, 2).text()).toContain(trigger.owner.name);
- });
+ await revealHideButton.vm.$emit('click');
- it('displays a "copy to cliboard" button for exposed tokens', () => {
- expect(findClipboardBtn(0).exists()).toBe(true);
- expect(findClipboardBtn(0).props('text')).toBe(triggers[0].token);
+ expect(revealHideButton.text()).toBe('Hide values');
+ });
- expect(findClipboardBtn(1).exists()).toBe(false);
- });
+ it('displays a table with rows', async () => {
+ await findRevealHideButton().vm.$emit('click');
- it('displays an "invalid" label for tokens without access', () => {
- expect(findInvalidBadge(0).exists()).toBe(false);
+ expect(findRows()).toHaveLength(triggers.length);
- expect(findInvalidBadge(1).exists()).toBe(true);
- });
+ const [trigger] = triggers;
- it('displays a time ago label when last used', () => {
- expect(findCell(0, 3).text()).toBe('Never');
+ expect(findCell(0, 0).text()).toBe(trigger.token);
+ expect(findCell(0, 1).text()).toBe(trigger.description);
+ expect(findCell(0, 2).text()).toContain(trigger.owner.name);
+ });
- expect(findCell(1, 3).findComponent(TimeAgoTooltip).props('time')).toBe(triggers[1].lastUsed);
- });
+ it('displays a "copy to cliboard" button for exposed tokens', () => {
+ expect(findClipboardBtn(0).exists()).toBe(true);
+ expect(findClipboardBtn(0).props('text')).toBe(triggers[0].token);
- it('displays actions in a rows', () => {
- const [data] = triggers;
- const confirmWarning =
- 'By revoking a trigger you will break any processes making use of it. Are you sure?';
+ expect(findClipboardBtn(1).exists()).toBe(false);
+ });
+
+ it('displays an "invalid" label for tokens without access', () => {
+ expect(findInvalidBadge(0).exists()).toBe(false);
- expect(findEditBtn(0).attributes('href')).toBe(data.editProjectTriggerPath);
+ expect(findInvalidBadge(1).exists()).toBe(true);
+ });
- expect(findRevokeBtn(0).attributes('href')).toBe(data.projectTriggerPath);
- expect(findRevokeBtn(0).attributes('data-method')).toBe('delete');
- expect(findRevokeBtn(0).attributes('data-confirm')).toBe(confirmWarning);
+ it('displays a time ago label when last used', () => {
+ expect(findCell(0, 3).text()).toBe('Never');
+
+ expect(findCell(1, 3).findComponent(TimeAgoTooltip).props('time')).toBe(triggers[1].lastUsed);
+ });
+
+ it('displays actions in a rows', () => {
+ const [data] = triggers;
+ const confirmWarning =
+ 'By revoking a trigger you will break any processes making use of it. Are you sure?';
+
+ expect(findEditBtn(0).attributes('href')).toBe(data.editProjectTriggerPath);
+
+ expect(findRevokeBtn(0).attributes('href')).toBe(data.projectTriggerPath);
+ expect(findRevokeBtn(0).attributes('data-method')).toBe('delete');
+ expect(findRevokeBtn(0).attributes('data-confirm')).toBe(confirmWarning);
+ });
});
describe('when there are no triggers set', () => {
diff --git a/spec/frontend/commits_spec.js b/spec/frontend/commits_spec.js
index db1516ed4ec..c79170aa37e 100644
--- a/spec/frontend/commits_spec.js
+++ b/spec/frontend/commits_spec.js
@@ -4,6 +4,7 @@ import 'vendor/jquery.endless-scroll';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import CommitsList from '~/commits';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import Pager from '~/pager';
describe('Commits List', () => {
@@ -64,7 +65,7 @@ describe('Commits List', () => {
jest.spyOn(window.history, 'replaceState').mockImplementation(() => {});
mock = new MockAdapter(axios);
- mock.onGet('/h5bp/html5-boilerplate/commits/main').reply(200, {
+ mock.onGet('/h5bp/html5-boilerplate/commits/main').reply(HTTP_STATUS_OK, {
html: '<li>Result</li>',
});
diff --git a/spec/frontend/merge_request_spec.js b/spec/frontend/merge_request_spec.js
index 7333ce345eb..579cee8c022 100644
--- a/spec/frontend/merge_request_spec.js
+++ b/spec/frontend/merge_request_spec.js
@@ -5,7 +5,7 @@ import { TEST_HOST } from 'spec/test_constants';
import waitForPromises from 'helpers/wait_for_promises';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_CONFLICT } from '~/lib/utils/http_status';
+import { HTTP_STATUS_CONFLICT, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import MergeRequest from '~/merge_request';
jest.mock('~/flash');
@@ -23,7 +23,7 @@ describe('MergeRequest', () => {
mock
.onPatch(`${TEST_HOST}/frontend-fixtures/merge-requests-project/-/merge_requests/1.json`)
- .reply(200, {});
+ .reply(HTTP_STATUS_OK, {});
test.merge = new MergeRequest();
return test.merge;
diff --git a/spec/frontend/merge_requests/components/compare_app_spec.js b/spec/frontend/merge_requests/components/compare_app_spec.js
new file mode 100644
index 00000000000..8f84341b653
--- /dev/null
+++ b/spec/frontend/merge_requests/components/compare_app_spec.js
@@ -0,0 +1,50 @@
+import { shallowMount } from '@vue/test-utils';
+import CompareApp from '~/merge_requests/components/compare_app.vue';
+
+let wrapper;
+
+function factory(provideData = {}) {
+ wrapper = shallowMount(CompareApp, {
+ provide: {
+ inputs: {
+ project: {
+ id: 'project',
+ name: 'project',
+ },
+ branch: {
+ id: 'branch',
+ name: 'branch',
+ },
+ },
+ toggleClass: {
+ project: 'project',
+ branch: 'branch',
+ },
+ i18n: {
+ projectHeaderText: 'Project',
+ branchHeaderText: 'Branch',
+ },
+ ...provideData,
+ },
+ });
+}
+
+describe('Merge requests compare app component', () => {
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('shows commit box when selected branch is empty', () => {
+ factory({
+ currentBranch: {
+ text: '',
+ value: '',
+ },
+ });
+
+ const commitBox = wrapper.find('[data-testid="commit-box"]');
+
+ expect(commitBox.exists()).toBe(true);
+ expect(commitBox.text()).toBe('Select a branch to compare');
+ });
+});
diff --git a/spec/frontend/pager_spec.js b/spec/frontend/pager_spec.js
index dfb3e87a342..2904ef547fe 100644
--- a/spec/frontend/pager_spec.js
+++ b/spec/frontend/pager_spec.js
@@ -4,6 +4,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import { removeParams } from '~/lib/utils/url_utility';
import Pager from '~/pager';
@@ -49,7 +50,7 @@ describe('pager', () => {
const urlRegex = /(.*)some_list(.*)$/;
function mockSuccess(count = 0) {
- axiosMock.onGet(urlRegex).reply(200, {
+ axiosMock.onGet(urlRegex).reply(HTTP_STATUS_OK, {
count,
html: '',
});
diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js
index 17ff0969b30..518539d97ba 100644
--- a/spec/frontend/pipelines/pipelines_spec.js
+++ b/spec/frontend/pipelines/pipelines_spec.js
@@ -13,7 +13,7 @@ import waitForPromises from 'helpers/wait_for_promises';
import Api from '~/api';
import { createAlert, VARIANT_WARNING } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
+import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import NavigationControls from '~/pipelines/components/pipelines_list/nav_controls.vue';
import PipelinesComponent from '~/pipelines/components/pipelines_list/pipelines.vue';
import PipelinesCiTemplates from '~/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue';
@@ -142,7 +142,7 @@ describe('Pipelines', () => {
beforeEach(() => {
mock
.onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } })
- .reply(200, mockPipelinesResponse);
+ .reply(HTTP_STATUS_OK, mockPipelinesResponse);
});
describe('when user has no permissions', () => {
@@ -234,7 +234,7 @@ describe('Pipelines', () => {
beforeEach(async () => {
mock
.onGet(mockPipelinesEndpoint, { params: { scope: 'finished', page: '1' } })
- .reply(200, {
+ .reply(HTTP_STATUS_OK, {
pipelines: [mockFinishedPipeline],
count: mockPipelinesResponse.count,
});
@@ -278,7 +278,7 @@ describe('Pipelines', () => {
beforeEach(async () => {
mock
.onGet(mockPipelinesEndpoint, { params: { scope: 'branches', page: '1' } })
- .reply(200, {
+ .reply(HTTP_STATUS_OK, {
pipelines: [],
count: mockPipelinesResponse.count,
});
@@ -321,7 +321,7 @@ describe('Pipelines', () => {
.onGet(mockPipelinesEndpoint, {
params: expectedParams,
})
- .replyOnce(200, {
+ .replyOnce(HTTP_STATUS_OK, {
pipelines: [mockFilteredPipeline],
count: mockPipelinesResponse.count,
});
@@ -399,7 +399,7 @@ describe('Pipelines', () => {
beforeEach(async () => {
mock.onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } }).reply(
- 200,
+ HTTP_STATUS_OK,
{
pipelines: firstPage,
count: mockPipelinesResponse.count,
@@ -407,7 +407,7 @@ describe('Pipelines', () => {
mockPageHeaders({ page: 1 }),
);
mock.onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '2' } }).reply(
- 200,
+ HTTP_STATUS_OK,
{
pipelines: secondPage,
count: mockPipelinesResponse.count,
@@ -462,13 +462,13 @@ describe('Pipelines', () => {
// Mock no pipelines in the first attempt
mock
.onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } })
- .replyOnce(200, emptyResponse, {
+ .replyOnce(HTTP_STATUS_OK, emptyResponse, {
'POLL-INTERVAL': 100,
});
// Mock pipelines in the next attempt
mock
.onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } })
- .reply(200, mockPipelinesResponse, {
+ .reply(HTTP_STATUS_OK, mockPipelinesResponse, {
'POLL-INTERVAL': 100,
});
});
@@ -509,10 +509,12 @@ describe('Pipelines', () => {
describe('when no pipelines exist', () => {
beforeEach(() => {
- mock.onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } }).reply(200, {
- pipelines: [],
- count: { all: '0' },
- });
+ mock
+ .onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } })
+ .reply(HTTP_STATUS_OK, {
+ pipelines: [],
+ count: { all: '0' },
+ });
});
describe('when CI is enabled and user has permissions', () => {
@@ -551,10 +553,12 @@ describe('Pipelines', () => {
});
it('renders tab empty state finished scope', async () => {
- mock.onGet(mockPipelinesEndpoint, { params: { scope: 'finished', page: '1' } }).reply(200, {
- pipelines: [],
- count: { all: '0' },
- });
+ mock
+ .onGet(mockPipelinesEndpoint, { params: { scope: 'finished', page: '1' } })
+ .reply(HTTP_STATUS_OK, {
+ pipelines: [],
+ count: { all: '0' },
+ });
findNavigationTabs().vm.$emit('onChangeTab', 'finished');
@@ -644,7 +648,7 @@ describe('Pipelines', () => {
beforeEach(() => {
mock.onGet(mockPipelinesEndpoint, { scope: 'all', page: '1' }).reply(
- 200,
+ HTTP_STATUS_OK,
{
pipelines: [mockPipelineWithStages],
count: { all: '1' },
@@ -654,7 +658,9 @@ describe('Pipelines', () => {
},
);
- mock.onGet(mockPipelineWithStages.details.stages[0].dropdown_path).reply(200, stageReply);
+ mock
+ .onGet(mockPipelineWithStages.details.stages[0].dropdown_path)
+ .reply(HTTP_STATUS_OK, stageReply);
createComponent();
@@ -665,7 +671,7 @@ describe('Pipelines', () => {
describe('when a request is being made', () => {
beforeEach(async () => {
- mock.onGet(mockPipelinesEndpoint).reply(200, mockPipelinesResponse);
+ mock.onGet(mockPipelinesEndpoint).reply(HTTP_STATUS_OK, mockPipelinesResponse);
await waitForPromises();
});
diff --git a/spec/frontend/pipelines/test_reports/stores/actions_spec.js b/spec/frontend/pipelines/test_reports/stores/actions_spec.js
index 6e61ef97257..f6287107ed0 100644
--- a/spec/frontend/pipelines/test_reports/stores/actions_spec.js
+++ b/spec/frontend/pipelines/test_reports/stores/actions_spec.js
@@ -4,6 +4,7 @@ import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import * as actions from '~/pipelines/stores/test_reports/actions';
import * as types from '~/pipelines/stores/test_reports/mutation_types';
@@ -35,7 +36,7 @@ describe('Actions TestReports Store', () => {
describe('fetch report summary', () => {
beforeEach(() => {
- mock.onGet(summaryEndpoint).replyOnce(200, summary, {});
+ mock.onGet(summaryEndpoint).replyOnce(HTTP_STATUS_OK, summary, {});
});
it('sets testReports and shows tests', () => {
@@ -66,7 +67,7 @@ describe('Actions TestReports Store', () => {
testReports.test_suites[0].build_ids = buildIds;
mock
.onGet(suiteEndpoint, { params: { build_ids: buildIds } })
- .replyOnce(200, testReports.test_suites[0], {});
+ .replyOnce(HTTP_STATUS_OK, testReports.test_suites[0], {});
});
it('sets test suite and shows tests', () => {
diff --git a/spec/frontend/projects/commits/store/actions_spec.js b/spec/frontend/projects/commits/store/actions_spec.js
index 64eba056c6f..bae9c48fc1e 100644
--- a/spec/frontend/projects/commits/store/actions_spec.js
+++ b/spec/frontend/projects/commits/store/actions_spec.js
@@ -2,7 +2,7 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import { createAlert } from '~/flash';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
+import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import actions from '~/projects/commits/store/actions';
import * as types from '~/projects/commits/store/mutation_types';
import createState from '~/projects/commits/store/state';
@@ -52,7 +52,7 @@ describe('Project commits actions', () => {
state.projectId = '8';
const data = [{ id: 1 }];
- mock.onGet(path).replyOnce(200, data);
+ mock.onGet(path).replyOnce(HTTP_STATUS_OK, data);
testAction(
actions.fetchAuthors,
null,
diff --git a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js
index 6152965398d..53763bd7d8f 100644
--- a/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js
+++ b/spec/frontend/projects/compare/components/revision_dropdown_legacy_spec.js
@@ -4,7 +4,7 @@ import AxiosMockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status';
+import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import RevisionDropdown from '~/projects/compare/components/revision_dropdown_legacy.vue';
const defaultProps = {
@@ -51,7 +51,7 @@ describe('RevisionDropdown component', () => {
const Branches = ['branch-1', 'branch-2'];
const Tags = ['tag-1', 'tag-2', 'tag-3'];
- axiosMock.onGet(defaultProps.refsProjectPath).replyOnce(200, {
+ axiosMock.onGet(defaultProps.refsProjectPath).replyOnce(HTTP_STATUS_OK, {
Branches,
Tags,
});
@@ -65,7 +65,7 @@ describe('RevisionDropdown component', () => {
});
it('sets branches and tags to be an empty array when no tags or branches are given', async () => {
- axiosMock.onGet(defaultProps.refsProjectPath).replyOnce(200, {
+ axiosMock.onGet(defaultProps.refsProjectPath).replyOnce(HTTP_STATUS_OK, {
Branches: undefined,
Tags: undefined,
});
diff --git a/spec/frontend/projects/compare/components/revision_dropdown_spec.js b/spec/frontend/projects/compare/components/revision_dropdown_spec.js
index d1f8fe96b86..db4a1158996 100644
--- a/spec/frontend/projects/compare/components/revision_dropdown_spec.js
+++ b/spec/frontend/projects/compare/components/revision_dropdown_spec.js
@@ -4,7 +4,7 @@ import AxiosMockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status';
+import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import RevisionDropdown from '~/projects/compare/components/revision_dropdown.vue';
import { revisionDropdownDefaultProps as defaultProps } from './mock_data';
@@ -50,7 +50,7 @@ describe('RevisionDropdown component', () => {
const Branches = ['branch-1', 'branch-2'];
const Tags = ['tag-1', 'tag-2', 'tag-3'];
- axiosMock.onGet(defaultProps.refsProjectPath).replyOnce(200, {
+ axiosMock.onGet(defaultProps.refsProjectPath).replyOnce(HTTP_STATUS_OK, {
Branches,
Tags,
});
diff --git a/spec/frontend/protected_branches/protected_branch_edit_spec.js b/spec/frontend/protected_branches/protected_branch_edit_spec.js
index 8e1a09500c7..b4029d94980 100644
--- a/spec/frontend/protected_branches/protected_branch_edit_spec.js
+++ b/spec/frontend/protected_branches/protected_branch_edit_spec.js
@@ -4,7 +4,7 @@ import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
+import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import ProtectedBranchEdit from '~/protected_branches/protected_branch_edit';
jest.mock('~/flash');
@@ -116,7 +116,9 @@ describe('ProtectedBranchEdit', () => {
describe('when clicked', () => {
beforeEach(async () => {
- mock.onPatch(TEST_URL, { protected_branch: { [patchParam]: true } }).replyOnce(200, {});
+ mock
+ .onPatch(TEST_URL, { protected_branch: { [patchParam]: true } })
+ .replyOnce(HTTP_STATUS_OK, {});
});
it('checks and disables button', async () => {
diff --git a/spec/frontend/search_autocomplete_spec.js b/spec/frontend/search_autocomplete_spec.js
index 266f047e9dc..a3098fb81ea 100644
--- a/spec/frontend/search_autocomplete_spec.js
+++ b/spec/frontend/search_autocomplete_spec.js
@@ -5,6 +5,7 @@ import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
import axios from '~/lib/utils/axios_utils';
import initSearchAutocomplete from '~/search_autocomplete';
import '~/lib/utils/common_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
describe('Search autocomplete dropdown', () => {
let widget = null;
@@ -191,7 +192,7 @@ describe('Search autocomplete dropdown', () => {
const axiosMock = new AxiosMockAdapter(axios);
const autocompleteUrl = new RegExp(autocompletePath);
- axiosMock.onGet(autocompleteUrl).reply(200, [
+ axiosMock.onGet(autocompleteUrl).reply(HTTP_STATUS_OK, [
{
category: 'Projects',
id: 1,
diff --git a/spec/frontend/sidebar/components/labels/labels_select_vue/store/actions_spec.js b/spec/frontend/sidebar/components/labels/labels_select_vue/store/actions_spec.js
index 56d516bf589..55651bccaa8 100644
--- a/spec/frontend/sidebar/components/labels/labels_select_vue/store/actions_spec.js
+++ b/spec/frontend/sidebar/components/labels/labels_select_vue/store/actions_spec.js
@@ -3,7 +3,7 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
+import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import * as actions from '~/sidebar/components/labels/labels_select_vue/store/actions';
import * as types from '~/sidebar/components/labels/labels_select_vue/store/mutation_types';
import defaultState from '~/sidebar/components/labels/labels_select_vue/store/state';
@@ -122,7 +122,7 @@ describe('LabelsSelect Actions', () => {
describe('on success', () => {
it('dispatches `requestLabels` & `receiveLabelsSuccess` actions', () => {
const labels = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
- mock.onGet(/labels.json/).replyOnce(200, labels);
+ mock.onGet(/labels.json/).replyOnce(HTTP_STATUS_OK, labels);
return testAction(
actions.fetchLabels,
@@ -206,7 +206,7 @@ describe('LabelsSelect Actions', () => {
describe('on success', () => {
it('dispatches `requestCreateLabel`, `fetchLabels` & `receiveCreateLabelSuccess` & `toggleDropdownContentsCreateView` actions', () => {
const label = { id: 1 };
- mock.onPost(/labels.json/).replyOnce(200, label);
+ mock.onPost(/labels.json/).replyOnce(HTTP_STATUS_OK, label);
return testAction(
actions.createLabel,
diff --git a/spec/frontend/vue_merge_request_widget/stores/artifacts_list/actions_spec.js b/spec/frontend/vue_merge_request_widget/stores/artifacts_list/actions_spec.js
index dd23d4fd3f5..be31c65b5e2 100644
--- a/spec/frontend/vue_merge_request_widget/stores/artifacts_list/actions_spec.js
+++ b/spec/frontend/vue_merge_request_widget/stores/artifacts_list/actions_spec.js
@@ -2,7 +2,11 @@ import MockAdapter from 'axios-mock-adapter';
import { TEST_HOST } from 'helpers/test_constants';
import testAction from 'helpers/vuex_action_helper';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_NO_CONTENT } from '~/lib/utils/http_status';
+import {
+ HTTP_STATUS_INTERNAL_SERVER_ERROR,
+ HTTP_STATUS_NO_CONTENT,
+ HTTP_STATUS_OK,
+} from '~/lib/utils/http_status';
import {
setEndpoint,
requestArtifacts,
@@ -62,7 +66,7 @@ describe('Artifacts App Store Actions', () => {
describe('success', () => {
it('dispatches requestArtifacts and receiveArtifactsSuccess', () => {
- mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, [
+ mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(HTTP_STATUS_OK, [
{
text: 'result.txt',
url: 'asda',
@@ -90,7 +94,7 @@ describe('Artifacts App Store Actions', () => {
job_path: 'asda',
},
],
- status: 200,
+ status: HTTP_STATUS_OK,
},
type: 'receiveArtifactsSuccess',
},
@@ -127,7 +131,7 @@ describe('Artifacts App Store Actions', () => {
it('should commit RECEIVE_ARTIFACTS_SUCCESS mutation with 200', () => {
return testAction(
receiveArtifactsSuccess,
- { data: { summary: {} }, status: 200 },
+ { data: { summary: {} }, status: HTTP_STATUS_OK },
mockedState,
[{ type: types.RECEIVE_ARTIFACTS_SUCCESS, payload: { summary: {} } }],
[],
diff --git a/spec/frontend/vue_shared/alert_details/sidebar/alert_sidebar_assignees_spec.js b/spec/frontend/vue_shared/alert_details/sidebar/alert_sidebar_assignees_spec.js
index 5a0ee5a59ba..98a357bac2b 100644
--- a/spec/frontend/vue_shared/alert_details/sidebar/alert_sidebar_assignees_spec.js
+++ b/spec/frontend/vue_shared/alert_details/sidebar/alert_sidebar_assignees_spec.js
@@ -3,6 +3,7 @@ import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import SidebarAssignee from '~/vue_shared/alert_details/components/sidebar/sidebar_assignee.vue';
import SidebarAssignees from '~/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue';
import AlertSetAssignees from '~/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql';
@@ -97,7 +98,7 @@ describe('Alert Details Sidebar Assignees', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
- mock.onGet(mockPath).replyOnce(200, mockUsers);
+ mock.onGet(mockPath).replyOnce(HTTP_STATUS_OK, mockUsers);
mountComponent({
data: { alert: mockAlert },
sidebarCollapsed: false,
@@ -187,7 +188,7 @@ describe('Alert Details Sidebar Assignees', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
- mock.onGet(mockPath).replyOnce(200, mockUsers);
+ mock.onGet(mockPath).replyOnce(HTTP_STATUS_OK, mockUsers);
mountComponent({
data: { alert: mockAlert },
diff --git a/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js b/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js
index ecbd322e17b..b0c0fc79676 100644
--- a/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js
+++ b/spec/frontend/vue_shared/components/content_viewer/viewers/markdown_viewer_spec.js
@@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
+import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import MarkdownViewer from '~/vue_shared/components/content_viewer/viewers/markdown_viewer.vue';
jest.mock('~/behaviors/markdown/render_gfm');
@@ -36,9 +36,11 @@ describe('MarkdownViewer', () => {
describe('success', () => {
beforeEach(() => {
- mock.onPost(`${gon.relative_url_root}/testproject/preview_markdown`).replyOnce(200, {
- body: '<b>testing</b> {{gl_md_img_1}}',
- });
+ mock
+ .onPost(`${gon.relative_url_root}/testproject/preview_markdown`)
+ .replyOnce(HTTP_STATUS_OK, {
+ body: '<b>testing</b> {{gl_md_img_1}}',
+ });
});
it('renders a skeleton loader while the markdown is loading', () => {
diff --git a/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js b/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
index e6e9e486b11..221da35de3d 100644
--- a/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
+++ b/spec/frontend/vue_shared/security_reports/security_reports_app_spec.js
@@ -16,7 +16,7 @@ import {
} from 'jest/vue_shared/security_reports/mock_data';
import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR } from '~/lib/utils/http_status';
+import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import HelpIcon from '~/vue_shared/security_reports/components/help_icon.vue';
import SecurityReportDownloadDropdown from '~/vue_shared/security_reports/components/security_report_download_dropdown.vue';
import {
@@ -188,7 +188,7 @@ describe('Security reports app', () => {
describe('when loading', () => {
beforeEach(() => {
mock = new MockAdapter(axios, { delayResponse: 1 });
- mock.onGet(path).replyOnce(200, successResponse);
+ mock.onGet(path).replyOnce(HTTP_STATUS_OK, successResponse);
createComponentWithFlagEnabled({
propsData: {
@@ -210,7 +210,7 @@ describe('Security reports app', () => {
describe('when successfully loaded', () => {
beforeEach(() => {
- mock.onGet(path).replyOnce(200, successResponse);
+ mock.onGet(path).replyOnce(HTTP_STATUS_OK, successResponse);
createComponentWithFlagEnabled({
propsData: {
diff --git a/spec/frontend/vue_shared/security_reports/store/modules/sast/actions_spec.js b/spec/frontend/vue_shared/security_reports/store/modules/sast/actions_spec.js
index d8474708d6c..0cab950cb77 100644
--- a/spec/frontend/vue_shared/security_reports/store/modules/sast/actions_spec.js
+++ b/spec/frontend/vue_shared/security_reports/store/modules/sast/actions_spec.js
@@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status';
+import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import * as actions from '~/vue_shared/security_reports/store/modules/sast/actions';
import * as types from '~/vue_shared/security_reports/store/modules/sast/mutation_types';
import createState from '~/vue_shared/security_reports/store/modules/sast/state';
@@ -100,9 +100,9 @@ describe('sast report actions', () => {
beforeEach(() => {
mock
.onGet(diffEndpoint)
- .replyOnce(200, reports.diff)
+ .replyOnce(HTTP_STATUS_OK, reports.diff)
.onGet(vulnerabilityFeedbackPath)
- .replyOnce(200, reports.enrichData);
+ .replyOnce(HTTP_STATUS_OK, reports.enrichData);
});
it('should dispatch the `receiveDiffSuccess` action', () => {
@@ -129,7 +129,7 @@ describe('sast report actions', () => {
describe('when diff endpoint responds successfully and fetching vulnerability feedback is not authorized', () => {
beforeEach(() => {
rootState.canReadVulnerabilityFeedback = false;
- mock.onGet(diffEndpoint).replyOnce(200, reports.diff);
+ mock.onGet(diffEndpoint).replyOnce(HTTP_STATUS_OK, reports.diff);
});
it('should dispatch the `receiveDiffSuccess` action with empty enrich data', () => {
@@ -158,7 +158,7 @@ describe('sast report actions', () => {
beforeEach(() => {
mock
.onGet(diffEndpoint)
- .replyOnce(200, reports.diff)
+ .replyOnce(HTTP_STATUS_OK, reports.diff)
.onGet(vulnerabilityFeedbackPath)
.replyOnce(HTTP_STATUS_NOT_FOUND);
});
@@ -180,7 +180,7 @@ describe('sast report actions', () => {
.onGet(diffEndpoint)
.replyOnce(HTTP_STATUS_NOT_FOUND)
.onGet(vulnerabilityFeedbackPath)
- .replyOnce(200, reports.enrichData);
+ .replyOnce(HTTP_STATUS_OK, reports.enrichData);
});
it('should dispatch the `receiveDiffError` action', () => {
diff --git a/spec/frontend/vue_shared/security_reports/store/modules/secret_detection/actions_spec.js b/spec/frontend/vue_shared/security_reports/store/modules/secret_detection/actions_spec.js
index d0a8468ad92..7197784c3e8 100644
--- a/spec/frontend/vue_shared/security_reports/store/modules/secret_detection/actions_spec.js
+++ b/spec/frontend/vue_shared/security_reports/store/modules/secret_detection/actions_spec.js
@@ -2,7 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import testAction from 'helpers/vuex_action_helper';
import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_NOT_FOUND } from '~/lib/utils/http_status';
+import { HTTP_STATUS_NOT_FOUND, HTTP_STATUS_OK } from '~/lib/utils/http_status';
import * as actions from '~/vue_shared/security_reports/store/modules/secret_detection/actions';
import * as types from '~/vue_shared/security_reports/store/modules/secret_detection/mutation_types';
import createState from '~/vue_shared/security_reports/store/modules/secret_detection/state';
@@ -100,9 +100,9 @@ describe('secret detection report actions', () => {
beforeEach(() => {
mock
.onGet(diffEndpoint)
- .replyOnce(200, reports.diff)
+ .replyOnce(HTTP_STATUS_OK, reports.diff)
.onGet(vulnerabilityFeedbackPath)
- .replyOnce(200, reports.enrichData);
+ .replyOnce(HTTP_STATUS_OK, reports.enrichData);
});
it('should dispatch the `receiveDiffSuccess` action', () => {
@@ -130,7 +130,7 @@ describe('secret detection report actions', () => {
describe('when diff endpoint responds successfully and fetching vulnerability feedback is not authorized', () => {
beforeEach(() => {
rootState.canReadVulnerabilityFeedback = false;
- mock.onGet(diffEndpoint).replyOnce(200, reports.diff);
+ mock.onGet(diffEndpoint).replyOnce(HTTP_STATUS_OK, reports.diff);
});
it('should dispatch the `receiveDiffSuccess` action with empty enrich data', () => {
@@ -159,7 +159,7 @@ describe('secret detection report actions', () => {
beforeEach(() => {
mock
.onGet(diffEndpoint)
- .replyOnce(200, reports.diff)
+ .replyOnce(HTTP_STATUS_OK, reports.diff)
.onGet(vulnerabilityFeedbackPath)
.replyOnce(HTTP_STATUS_NOT_FOUND);
});
@@ -181,7 +181,7 @@ describe('secret detection report actions', () => {
.onGet(diffEndpoint)
.replyOnce(HTTP_STATUS_NOT_FOUND)
.onGet(vulnerabilityFeedbackPath)
- .replyOnce(200, reports.enrichData);
+ .replyOnce(HTTP_STATUS_OK, reports.enrichData);
});
it('should dispatch the `receiveDiffError` action', () => {
diff --git a/spec/frontend/whats_new/store/actions_spec.js b/spec/frontend/whats_new/store/actions_spec.js
index c9614c7330b..5f5e4e53be2 100644
--- a/spec/frontend/whats_new/store/actions_spec.js
+++ b/spec/frontend/whats_new/store/actions_spec.js
@@ -3,6 +3,7 @@ import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import testAction from 'helpers/vuex_action_helper';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
+import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
import actions from '~/whats_new/store/actions';
import * as types from '~/whats_new/store/mutation_types';
@@ -33,7 +34,7 @@ describe('whats new actions', () => {
axiosMock = new MockAdapter(axios);
axiosMock
.onGet('/-/whats_new')
- .replyOnce(200, [{ title: 'Whats New Drawer', url: 'www.url.com' }], {
+ .replyOnce(HTTP_STATUS_OK, [{ title: 'Whats New Drawer', url: 'www.url.com' }], {
'x-next-page': '2',
});
@@ -49,7 +50,7 @@ describe('whats new actions', () => {
axiosMock
.onGet('/-/whats_new', { params: { page: undefined, v: undefined } })
- .replyOnce(200, [{ title: 'GitLab Stories' }]);
+ .replyOnce(HTTP_STATUS_OK, [{ title: 'GitLab Stories' }]);
testAction(
actions.fetchItems,
@@ -66,7 +67,7 @@ describe('whats new actions', () => {
axiosMock
.onGet('/-/whats_new', { params: { page: 8, v: 42 } })
- .replyOnce(200, [{ title: 'GitLab Stories' }]);
+ .replyOnce(HTTP_STATUS_OK, [{ title: 'GitLab Stories' }]);
testAction(
actions.fetchItems,
diff --git a/spec/graphql/types/ci/pipeline_type_spec.rb b/spec/graphql/types/ci/pipeline_type_spec.rb
index 5683b3f86c4..67209874b54 100644
--- a/spec/graphql/types/ci/pipeline_type_spec.rb
+++ b/spec/graphql/types/ci/pipeline_type_spec.rb
@@ -20,7 +20,7 @@ RSpec.describe Types::Ci::PipelineType do
if Gitlab.ee?
expected_fields += %w[
security_report_summary security_report_findings security_report_finding
- code_quality_reports dast_profile
+ code_quality_reports dast_profile code_quality_report_summary
]
end
diff --git a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
index 68e70525c55..93644aa1497 100644
--- a/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
+++ b/spec/lib/gitlab/ci/reports/codequality_reports_spec.rb
@@ -4,19 +4,25 @@ require 'spec_helper'
RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
let(:codequality_report) { described_class.new }
- let(:degradation_1) { build(:codequality_degradation_1) }
- let(:degradation_2) { build(:codequality_degradation_2) }
+ let(:degradation_major) { build(:codequality_degradation, :major) }
+ let(:degradation_minor) { build(:codequality_degradation, :minor) }
+ let(:degradation_blocker) { build(:codequality_degradation, :blocker) }
+ let(:degradation_info) { build(:codequality_degradation, :info) }
+ let(:degradation_major_2) { build(:codequality_degradation, :major) }
+ let(:degradation_critical) { build(:codequality_degradation, :critical) }
+ let(:degradation_uppercase_major) { build(:codequality_degradation, severity: 'MAJOR') }
+ let(:degradation_unknown) { build(:codequality_degradation, severity: 'unknown') }
it { expect(codequality_report.degradations).to eq({}) }
describe '#add_degradation' do
context 'when there is a degradation' do
before do
- codequality_report.add_degradation(degradation_1)
+ codequality_report.add_degradation(degradation_major)
end
it 'adds degradation to codequality report' do
- expect(codequality_report.degradations.keys).to eq([degradation_1[:fingerprint]])
+ expect(codequality_report.degradations.keys).to match_array([degradation_major[:fingerprint]])
expect(codequality_report.degradations.values.size).to eq(1)
end
end
@@ -53,8 +59,8 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
context 'when there are many degradations' do
before do
- codequality_report.add_degradation(degradation_1)
- codequality_report.add_degradation(degradation_2)
+ codequality_report.add_degradation(degradation_major)
+ codequality_report.add_degradation(degradation_minor)
end
it 'returns the number of degradations' do
@@ -68,36 +74,25 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
context 'when there are many degradations' do
before do
- codequality_report.add_degradation(degradation_1)
- codequality_report.add_degradation(degradation_2)
+ codequality_report.add_degradation(degradation_major)
+ codequality_report.add_degradation(degradation_minor)
end
it 'returns all degradations' do
- expect(all_degradations).to contain_exactly(degradation_1, degradation_2)
+ expect(all_degradations).to contain_exactly(degradation_major, degradation_minor)
end
end
end
describe '#sort_degradations!' do
- let(:major) { build(:codequality_degradation, :major) }
- let(:minor) { build(:codequality_degradation, :minor) }
- let(:blocker) { build(:codequality_degradation, :blocker) }
- let(:info) { build(:codequality_degradation, :info) }
- let(:major_2) { build(:codequality_degradation, :major) }
- let(:critical) { build(:codequality_degradation, :critical) }
- let(:uppercase_major) { build(:codequality_degradation, severity: 'MAJOR') }
- let(:unknown) { build(:codequality_degradation, severity: 'unknown') }
-
- let(:codequality_report) { described_class.new }
-
before do
- codequality_report.add_degradation(major)
- codequality_report.add_degradation(minor)
- codequality_report.add_degradation(blocker)
- codequality_report.add_degradation(major_2)
- codequality_report.add_degradation(info)
- codequality_report.add_degradation(critical)
- codequality_report.add_degradation(unknown)
+ codequality_report.add_degradation(degradation_major)
+ codequality_report.add_degradation(degradation_minor)
+ codequality_report.add_degradation(degradation_blocker)
+ codequality_report.add_degradation(degradation_major_2)
+ codequality_report.add_degradation(degradation_info)
+ codequality_report.add_degradation(degradation_critical)
+ codequality_report.add_degradation(degradation_unknown)
codequality_report.sort_degradations!
end
@@ -105,36 +100,70 @@ RSpec.describe Gitlab::Ci::Reports::CodequalityReports do
it 'sorts degradations based on severity' do
expect(codequality_report.degradations.values).to eq(
[
- blocker,
- critical,
- major,
- major_2,
- minor,
- info,
- unknown
+ degradation_blocker,
+ degradation_critical,
+ degradation_major,
+ degradation_major_2,
+ degradation_minor,
+ degradation_info,
+ degradation_unknown
])
end
context 'with non-existence and uppercase severities' do
let(:other_report) { described_class.new }
- let(:non_existent) { build(:codequality_degradation, severity: 'non-existent') }
+ let(:degradation_non_existent) { build(:codequality_degradation, severity: 'non-existent') }
before do
- other_report.add_degradation(blocker)
- other_report.add_degradation(uppercase_major)
- other_report.add_degradation(minor)
- other_report.add_degradation(non_existent)
+ other_report.add_degradation(degradation_blocker)
+ other_report.add_degradation(degradation_uppercase_major)
+ other_report.add_degradation(degradation_minor)
+ other_report.add_degradation(degradation_non_existent)
end
it 'sorts unknown last' do
expect(other_report.degradations.values).to eq(
[
- blocker,
- uppercase_major,
- minor,
- non_existent
+ degradation_blocker,
+ degradation_uppercase_major,
+ degradation_minor,
+ degradation_non_existent
])
end
end
end
+
+ describe '#code_quality_report_summary' do
+ context "when there is no degradation" do
+ it 'return nil' do
+ expect(codequality_report.code_quality_report_summary).to eq(nil)
+ end
+ end
+
+ context "when there are degradations" do
+ before do
+ codequality_report.add_degradation(degradation_major)
+ codequality_report.add_degradation(degradation_major_2)
+ codequality_report.add_degradation(degradation_minor)
+ codequality_report.add_degradation(degradation_blocker)
+ codequality_report.add_degradation(degradation_info)
+ codequality_report.add_degradation(degradation_critical)
+ codequality_report.add_degradation(degradation_unknown)
+ end
+
+ it 'returns the summary of the code quality report' do
+ expect(codequality_report.code_quality_report_summary).to eq(
+ {
+ 'major' => 2,
+ 'minor' => 1,
+ 'blocker' => 1,
+ 'info' => 1,
+ 'critical' => 1,
+ 'unknown' => 1,
+ 'count' => 7
+ }
+ )
+ end
+ end
+ end
end
diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb
index 5ed4eb7d233..de67ec35587 100644
--- a/spec/models/release_spec.rb
+++ b/spec/models/release_spec.rb
@@ -81,15 +81,10 @@ RSpec.describe Release do
end
end
- # Mimic releases created before 11.7
- # See: https://gitlab.com/gitlab-org/gitlab/-/blob/8e5a110b01f842d8b6a702197928757a40ce9009/app/models/release.rb#L14
+ # Deleting user along with their contributions, nullifies releases author_id.
context 'when updating existing release without author' do
let(:release) { create(:release, :legacy) }
- before do
- stub_feature_flags(validate_release_with_author: false)
- end
-
it 'updates successfully' do
release.description += 'Update'