Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-03-25 06:09:02 +00:00
parent 3bd9ad5574
commit 807d57c18f
75 changed files with 368 additions and 200 deletions

View File

@ -274,7 +274,7 @@ gem 'licensee', '~> 9.14.1'
gem 'charlock_holmes', '~> 0.7.7' gem 'charlock_holmes', '~> 0.7.7'
# Detect mime content type from content # Detect mime content type from content
gem 'mimemagic', '~> 0.3.2' gem 'ruby-magic-static'
# Faster blank # Faster blank
gem 'fast_blank' gem 'fast_blank'

View File

@ -1109,6 +1109,7 @@ GEM
i18n i18n
ruby-fogbugz (0.2.1) ruby-fogbugz (0.2.1)
crack (~> 0.4) crack (~> 0.4)
ruby-magic-static (0.3.0)
ruby-prof (1.3.1) ruby-prof (1.3.1)
ruby-progressbar (1.11.0) ruby-progressbar (1.11.0)
ruby-saml (1.7.2) ruby-saml (1.7.2)
@ -1481,7 +1482,6 @@ DEPENDENCIES
marginalia (~> 1.10.0) marginalia (~> 1.10.0)
memory_profiler (~> 0.9) memory_profiler (~> 0.9)
method_source (~> 1.0) method_source (~> 1.0)
mimemagic (~> 0.3.2)
mini_magick (~> 4.10.1) mini_magick (~> 4.10.1)
minitest (~> 5.11.0) minitest (~> 5.11.0)
multi_json (~> 1.14.1) multi_json (~> 1.14.1)
@ -1555,6 +1555,7 @@ DEPENDENCIES
rspec_junit_formatter rspec_junit_formatter
rspec_profiling (~> 0.0.6) rspec_profiling (~> 0.0.6)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
ruby-magic-static
ruby-prof (~> 1.3.0) ruby-prof (~> 1.3.0)
ruby-progressbar (~> 1.10) ruby-progressbar (~> 1.10)
ruby_parser (~> 3.15) ruby_parser (~> 3.15)

View File

@ -158,7 +158,7 @@ export default {
data-testid="issue-blocked-icon" data-testid="issue-blocked-icon"
@mouseenter="handleMouseEnter" @mouseenter="handleMouseEnter"
/> />
<gl-popover :target="glIconId" placement="top" triggers="hover"> <gl-popover :target="glIconId" placement="top">
<template #title <template #title
><span data-testid="popover-title">{{ blockedLabel }}</span></template ><span data-testid="popover-title">{{ blockedLabel }}</span></template
> >

View File

@ -37,7 +37,7 @@ export default {
<template> <template>
<div id="popover-container"> <div id="popover-container">
<gl-popover :target="target" triggers="hover" placement="top" container="popover-container"> <gl-popover :target="target" placement="top" container="popover-container">
<div <div
class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-word-break-all" class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-word-break-all"
> >

View File

@ -34,7 +34,7 @@ export default {
<gl-icon name="status_warning" :size="24" class="gl-p-2" /> <gl-icon name="status_warning" :size="24" class="gl-p-2" />
<gl-popover :container="popoverId" :target="popoverId" placement="top" triggers="hover focus"> <gl-popover :container="popoverId" :target="popoverId" placement="top">
<template #title> <template #title>
<span class="gl-display-block gl-text-left">{{ errorContent.title }}</span> <span class="gl-display-block gl-text-left">{{ errorContent.title }}</span>
</template> </template>

View File

@ -71,7 +71,6 @@ export default {
ref="popover" ref="popover"
:target="$options.targetId" :target="$options.targetId"
:css-classes="['feature-highlight-popover']" :css-classes="['feature-highlight-popover']"
triggers="hover"
container="body" container="body"
placement="right" placement="right"
boundary="viewport" boundary="viewport"

View File

@ -60,11 +60,7 @@ export default {
</select> </select>
<span v-if="requestsWithWarnings.length"> <span v-if="requestsWithWarnings.length">
<span id="performance-bar-request-selector-warning" v-html="glEmojiTag('warning')"></span> <span id="performance-bar-request-selector-warning" v-html="glEmojiTag('warning')"></span>
<gl-popover <gl-popover target="performance-bar-request-selector-warning" :content="warningMessage" />
target="performance-bar-request-selector-warning"
:content="warningMessage"
triggers="hover focus"
/>
</span> </span>
</div> </div>
</template> </template>

View File

@ -37,6 +37,6 @@ export default {
<template> <template>
<span v-if="hasWarnings"> <span v-if="hasWarnings">
<span :id="htmlId" v-html="glEmojiTag('warning')"></span> <span :id="htmlId" v-html="glEmojiTag('warning')"></span>
<gl-popover :target="htmlId" :content="warningMessage" triggers="hover focus" /> <gl-popover :target="htmlId" :content="warningMessage" />
</span> </span>
</template> </template>

View File

@ -149,12 +149,7 @@ export default {
> >
{{ s__('mrWidget|Resolve conflicts') }} {{ s__('mrWidget|Resolve conflicts') }}
</gl-button> </gl-button>
<gl-popover <gl-popover v-if="showPopover" :target="() => $refs.popover" placement="top">
v-if="showPopover"
:target="() => $refs.popover"
placement="top"
triggers="hover focus"
>
<template #title> <template #title>
<div class="gl-font-weight-normal gl-font-base"> <div class="gl-font-weight-normal gl-font-base">
{{ $options.i18n.title }} {{ $options.i18n.title }}

View File

@ -124,7 +124,7 @@ export default {
}, },
pipeline() { pipeline() {
if (this.glFeatures.mergeRequestWidgetGraphql) { if (this.glFeatures.mergeRequestWidgetGraphql) {
return this.state.pipelines?.nodes?.[0]; return this.state.headPipeline;
} }
return this.mr.pipeline; return this.mr.pipeline;

View File

@ -11,11 +11,10 @@ query getState($projectPath: ID!, $iid: String!) {
mergeError mergeError
mergeStatus mergeStatus
mergeableDiscussionsState mergeableDiscussionsState
pipelines(first: 1) { headPipeline {
nodes { id
status status
warnings warnings
}
} }
shouldBeRebased shouldBeRebased
sourceBranchExists sourceBranchExists

View File

@ -30,13 +30,11 @@ fragment ReadyToMerge on Project {
message message
} }
} }
pipelines(first: 1) { headPipeline {
nodes { id
id status
status path
path active
active
}
} }
} }
} }

View File

@ -163,7 +163,7 @@ export default class MergeRequestStore {
setGraphqlData(project) { setGraphqlData(project) {
const { mergeRequest } = project; const { mergeRequest } = project;
const pipeline = mergeRequest.pipelines?.nodes?.[0]; const pipeline = mergeRequest.headPipeline;
this.projectArchived = project.archived; this.projectArchived = project.archived;
this.onlyAllowMergeIfPipelineSucceeds = project.onlyAllowMergeIfPipelineSucceeds; this.onlyAllowMergeIfPipelineSucceeds = project.onlyAllowMergeIfPipelineSucceeds;

View File

@ -21,7 +21,7 @@ import Tracking from '~/tracking';
import initUserPopovers from '~/user_popovers'; import initUserPopovers from '~/user_popovers';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { SEVERITY_LEVELS } from '../constants'; import { PAGE_CONFIG, SEVERITY_LEVELS } from '../constants';
import createIssueMutation from '../graphql/mutations/alert_issue_create.mutation.graphql'; import createIssueMutation from '../graphql/mutations/alert_issue_create.mutation.graphql';
import toggleSidebarStatusMutation from '../graphql/mutations/alert_sidebar_status.mutation.graphql'; import toggleSidebarStatusMutation from '../graphql/mutations/alert_sidebar_status.mutation.graphql';
import alertQuery from '../graphql/queries/alert_details.query.graphql'; import alertQuery from '../graphql/queries/alert_details.query.graphql';
@ -92,6 +92,9 @@ export default {
projectIssuesPath: { projectIssuesPath: {
default: '', default: '',
}, },
statuses: {
default: PAGE_CONFIG.OPERATIONS.STATUSES,
},
trackAlertsDetailsViewsOptions: { trackAlertsDetailsViewsOptions: {
default: null, default: null,
}, },
@ -367,7 +370,7 @@ export default {
> >
{{ alert.runbook }} {{ alert.runbook }}
</alert-summary-row> </alert-summary-row>
<alert-details-table :alert="alert" :loading="loading" /> <alert-details-table :alert="alert" :loading="loading" :statuses="statuses" />
</gl-tab> </gl-tab>
<gl-tab <gl-tab
v-if="!isThreatMonitoringPage" v-if="!isThreatMonitoringPage"

View File

@ -19,10 +19,6 @@ export default {
projectId: { projectId: {
default: '', default: '',
}, },
// TODO remove this limitation in https://gitlab.com/gitlab-org/gitlab/-/issues/296717
isThreatMonitoringPage: {
default: false,
},
}, },
props: { props: {
alert: { alert: {
@ -66,7 +62,6 @@ export default {
@alert-error="$emit('alert-error', $event)" @alert-error="$emit('alert-error', $event)"
/> />
<sidebar-status <sidebar-status
v-if="!isThreatMonitoringPage"
:project-path="projectPath" :project-path="projectPath"
:alert="alert" :alert="alert"
@toggle-sidebar="$emit('toggle-sidebar')" @toggle-sidebar="$emit('toggle-sidebar')"

View File

@ -3,6 +3,7 @@ import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import updateAlertStatusMutation from '~/graphql_shared/mutations/alert_status_update.mutation.graphql'; import updateAlertStatusMutation from '~/graphql_shared/mutations/alert_status_update.mutation.graphql';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import { PAGE_CONFIG } from '../constants';
export default { export default {
i18n: { i18n: {
@ -11,11 +12,6 @@ export default {
), ),
UPDATE_ALERT_STATUS_INSTRUCTION: s__('AlertManagement|Please try again.'), UPDATE_ALERT_STATUS_INSTRUCTION: s__('AlertManagement|Please try again.'),
}, },
statuses: {
TRIGGERED: s__('AlertManagement|Triggered'),
ACKNOWLEDGED: s__('AlertManagement|Acknowledged'),
RESOLVED: s__('AlertManagement|Resolved'),
},
components: { components: {
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
@ -42,6 +38,11 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
statuses: {
type: Object,
required: false,
default: () => PAGE_CONFIG.OPERATIONS.STATUSES,
},
}, },
computed: { computed: {
dropdownClass() { dropdownClass() {
@ -57,13 +58,13 @@ export default {
mutation: updateAlertStatusMutation, mutation: updateAlertStatusMutation,
variables: { variables: {
iid: this.alert.iid, iid: this.alert.iid,
status: status.toUpperCase(), status,
projectPath: this.projectPath, projectPath: this.projectPath,
}, },
}) })
.then((resp) => { .then((resp) => {
if (this.trackAlertStatusUpdateOptions) { if (this.trackAlertStatusUpdateOptions) {
this.trackStatusUpdate(status); this.trackStatusUpdate(this.statuses[status]);
} }
const errors = resp.data?.updateAlertStatus?.errors || []; const errors = resp.data?.updateAlertStatus?.errors || [];
@ -99,7 +100,7 @@ export default {
<gl-dropdown <gl-dropdown
ref="dropdown" ref="dropdown"
right right
:text="$options.statuses[alert.status]" :text="statuses[alert.status]"
class="w-100" class="w-100"
toggle-class="dropdown-menu-toggle" toggle-class="dropdown-menu-toggle"
@keydown.esc.native="$emit('hide-dropdown')" @keydown.esc.native="$emit('hide-dropdown')"
@ -110,12 +111,12 @@ export default {
</p> </p>
<div class="dropdown-content dropdown-body"> <div class="dropdown-content dropdown-body">
<gl-dropdown-item <gl-dropdown-item
v-for="(label, field) in $options.statuses" v-for="(label, field) in statuses"
:key="field" :key="field"
data-testid="statusDropdownItem" data-testid="statusDropdownItem"
:active="label.toUpperCase() === alert.status" :active="field === alert.status"
:active-class="'is-active'" :active-class="'is-active'"
@click="updateAlertStatus(label)" @click="updateAlertStatus(field)"
> >
{{ label }} {{ label }}
</gl-dropdown-item> </gl-dropdown-item>

View File

@ -1,14 +1,9 @@
<script> <script>
import { GlIcon, GlLoadingIcon, GlTooltip, GlSprintf } from '@gitlab/ui'; import { GlIcon, GlLoadingIcon, GlTooltip, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale'; import { PAGE_CONFIG } from '../../constants';
import AlertStatus from '../alert_status.vue'; import AlertStatus from '../alert_status.vue';
export default { export default {
statuses: {
TRIGGERED: s__('AlertManagement|Triggered'),
ACKNOWLEDGED: s__('AlertManagement|Acknowledged'),
RESOLVED: s__('AlertManagement|Resolved'),
},
components: { components: {
GlIcon, GlIcon,
GlLoadingIcon, GlLoadingIcon,
@ -16,6 +11,11 @@ export default {
GlSprintf, GlSprintf,
AlertStatus, AlertStatus,
}, },
inject: {
statuses: {
default: PAGE_CONFIG.OPERATIONS.STATUSES,
},
},
props: { props: {
projectPath: { projectPath: {
type: String, type: String,
@ -94,6 +94,7 @@ export default {
:project-path="projectPath" :project-path="projectPath"
:is-dropdown-showing="isDropdownShowing" :is-dropdown-showing="isDropdownShowing"
:is-sidebar="true" :is-sidebar="true"
:statuses="statuses"
@alert-error="$emit('alert-error', $event)" @alert-error="$emit('alert-error', $event)"
@hide-dropdown="hideDropdown" @hide-dropdown="hideDropdown"
@handle-updating="handleUpdating" @handle-updating="handleUpdating"
@ -103,14 +104,11 @@ export default {
<p <p
v-else-if="!isDropdownShowing" v-else-if="!isDropdownShowing"
class="value gl-m-0" class="value gl-m-0"
:class="{ 'no-value': !$options.statuses[alert.status] }" :class="{ 'no-value': !statuses[alert.status] }"
> >
<span <span v-if="statuses[alert.status]" class="gl-text-gray-500" data-testid="status">
v-if="$options.statuses[alert.status]" {{ statuses[alert.status] }}
class="gl-text-gray-500" </span>
data-testid="status"
>{{ $options.statuses[alert.status] }}</span
>
<span v-else> <span v-else>
{{ s__('AlertManagement|None') }} {{ s__('AlertManagement|None') }}
</span> </span>

View File

@ -13,6 +13,11 @@ export const SEVERITY_LEVELS = {
export const PAGE_CONFIG = { export const PAGE_CONFIG = {
OPERATIONS: { OPERATIONS: {
TITLE: 'OPERATIONS', TITLE: 'OPERATIONS',
STATUSES: {
TRIGGERED: s__('AlertManagement|Triggered'),
ACKNOWLEDGED: s__('AlertManagement|Acknowledged'),
RESOLVED: s__('AlertManagement|Resolved'),
},
// Tracks snowplow event when user views alert details // Tracks snowplow event when user views alert details
TRACK_ALERTS_DETAILS_VIEWS_OPTIONS: { TRACK_ALERTS_DETAILS_VIEWS_OPTIONS: {
category: 'Alert Management', category: 'Alert Management',
@ -27,5 +32,11 @@ export const PAGE_CONFIG = {
}, },
THREAT_MONITORING: { THREAT_MONITORING: {
TITLE: 'THREAT_MONITORING', TITLE: 'THREAT_MONITORING',
STATUSES: {
TRIGGERED: s__('ThreatMonitoring|Unreviewed'),
ACKNOWLEDGED: s__('ThreatMonitoring|In review'),
RESOLVED: s__('ThreatMonitoring|Resolved'),
IGNORED: s__('ThreatMonitoring|Dismissed'),
},
}, },
}; };

View File

@ -55,6 +55,7 @@ export default (selector) => {
page, page,
projectIssuesPath, projectIssuesPath,
projectId, projectId,
statuses: PAGE_CONFIG[page].STATUSES,
}; };
if (page === PAGE_CONFIG.OPERATIONS.TITLE) { if (page === PAGE_CONFIG.OPERATIONS.TITLE) {

View File

@ -7,6 +7,7 @@ import {
splitCamelCase, splitCamelCase,
} from '~/lib/utils/text_utility'; } from '~/lib/utils/text_utility';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { PAGE_CONFIG } from '~/vue_shared/alert_details/constants';
const thClass = 'gl-bg-transparent! gl-border-1! gl-border-b-solid! gl-border-gray-200!'; const thClass = 'gl-bg-transparent! gl-border-1! gl-border-b-solid! gl-border-gray-200!';
const tdClass = 'gl-border-gray-100! gl-p-5!'; const tdClass = 'gl-border-gray-100! gl-p-5!';
@ -42,6 +43,11 @@ export default {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
statuses: {
type: Object,
required: false,
default: () => PAGE_CONFIG.OPERATIONS.STATUSES,
},
}, },
fields: [ fields: [
{ {
@ -71,6 +77,8 @@ export default {
let value; let value;
if (fieldName === 'environment') { if (fieldName === 'environment') {
value = fieldValue?.name; value = fieldValue?.name;
} else if (fieldName === 'status') {
value = this.statuses[fieldValue] || fieldValue;
} else { } else {
value = fieldValue; value = fieldValue;
} }

View File

@ -26,7 +26,7 @@ export default {
<template> <template>
<span> <span>
<gl-button ref="popoverTrigger" variant="link" icon="question" tabindex="0" /> <gl-button ref="popoverTrigger" variant="link" icon="question" tabindex="0" />
<gl-popover triggers="hover focus" :target="() => $refs.popoverTrigger.$el" v-bind="options"> <gl-popover :target="() => $refs.popoverTrigger.$el" v-bind="options">
<template v-if="options.title" #title> <template v-if="options.title" #title>
<span v-safe-html="options.title"></span> <span v-safe-html="options.title"></span>
</template> </template>

View File

@ -60,7 +60,7 @@ export default {
<template> <template>
<!-- 200ms delay so not every mouseover triggers Popover --> <!-- 200ms delay so not every mouseover triggers Popover -->
<gl-popover :target="target" :delay="200" boundary="viewport" triggers="hover" placement="top"> <gl-popover :target="target" :delay="200" boundary="viewport" placement="top">
<div class="gl-p-3 gl-line-height-normal gl-display-flex" data-testid="user-popover"> <div class="gl-p-3 gl-line-height-normal gl-display-flex" data-testid="user-popover">
<div class="gl-p-2 flex-shrink-1"> <div class="gl-p-2 flex-shrink-1">
<user-avatar-image :img-src="user.avatarUrl" :size="60" css-classes="gl-mr-3!" /> <user-avatar-image :img-src="user.avatarUrl" :size="60" css-classes="gl-mr-3!" />

View File

@ -226,10 +226,10 @@ class Projects::JobsController < Projects::ApplicationController
end end
def raw_trace_content_disposition(raw_data) def raw_trace_content_disposition(raw_data)
mime_type = MimeMagic.by_magic(raw_data) mime_type = Gitlab::Utils::MimeType.from_string(raw_data)
# if mime_type is nil can also represent 'text/plain' # if mime_type is nil can also represent 'text/plain'
return 'inline' if mime_type.nil? || mime_type.type == 'text/plain' return 'inline' if mime_type.nil? || mime_type == 'text/plain'
'attachment' 'attachment'
end end

View File

@ -14,8 +14,6 @@ module Ci
BuildArchivedError = Class.new(StandardError) BuildArchivedError = Class.new(StandardError)
ignore_columns :artifacts_file, :artifacts_file_store, :artifacts_metadata, :artifacts_metadata_store, :artifacts_size, :commands, remove_after: '2019-12-15', remove_with: '12.7'
belongs_to :project, inverse_of: :builds belongs_to :project, inverse_of: :builds
belongs_to :runner belongs_to :runner
belongs_to :trigger_request belongs_to :trigger_request

View File

@ -45,8 +45,6 @@ module Ci
FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze
MINUTES_COST_FACTOR_FIELDS = %i[public_projects_minutes_cost_factor private_projects_minutes_cost_factor].freeze MINUTES_COST_FACTOR_FIELDS = %i[public_projects_minutes_cost_factor private_projects_minutes_cost_factor].freeze
ignore_column :is_shared, remove_after: '2019-12-15', remove_with: '12.6'
has_many :builds has_many :builds
has_many :runner_projects, inverse_of: :runner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :runner_projects, inverse_of: :runner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :projects, through: :runner_projects has_many :projects, through: :runner_projects

View File

@ -13,8 +13,6 @@ class DeployKey < Key
scope :are_public, -> { where(public: true) } scope :are_public, -> { where(public: true) }
scope :with_projects, -> { includes(deploy_keys_projects: { project: [:route, namespace: :route] }) } scope :with_projects, -> { includes(deploy_keys_projects: { project: [:route, namespace: :route] }) }
ignore_column :can_push, remove_after: '2019-12-15', remove_with: '12.6'
accepts_nested_attributes_for :deploy_keys_projects accepts_nested_attributes_for :deploy_keys_projects
def private? def private?

View File

@ -43,7 +43,7 @@ module ContentTypeWhitelist
def mime_magic_content_type(path) def mime_magic_content_type(path)
if path if path
File.open(path) do |file| File.open(path) do |file|
MimeMagic.by_magic(file).try(:type) || 'invalid/invalid' Gitlab::Utils::MimeType.from_io(file) || 'invalid/invalid'
end end
end end
rescue Errno::ENOENT rescue Errno::ENOENT

View File

@ -0,0 +1,5 @@
---
title: Generalize alert details status
merge_request: 56800
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Show popovers on hover and focus by default
merge_request: 56778
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Refactor MimeMagic calls to new MimeType class
merge_request: 57421
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: Remove direct mimemagic dependency
merge_request: 57387
author:
type: other

View File

@ -1148,7 +1148,7 @@ POST /projects
| `namespace_id` | integer | **{dotted-circle}** No | Namespace for the new project (defaults to the current user's namespace). | | `namespace_id` | integer | **{dotted-circle}** No | Namespace for the new project (defaults to the current user's namespace). |
| `operations_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. | | `operations_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, or `enabled`. |
| `only_allow_merge_if_all_discussions_are_resolved` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged when all the discussions are resolved. | | `only_allow_merge_if_all_discussions_are_resolved` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged when all the discussions are resolved. |
| `only_allow_merge_if_pipeline_succeeds` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged with successful jobs. | | `only_allow_merge_if_pipeline_succeeds` | boolean | **{dotted-circle}** No | Set whether merge requests can only be merged with successful pipelines. This setting is named [**Pipelines must succeed**](../user/project/merge_requests/merge_when_pipeline_succeeds.md#only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds) in the project settings. |
| `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. | | `packages_enabled` | boolean | **{dotted-circle}** No | Enable or disable packages repository feature. |
| `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. | | `pages_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled`, or `public`. |
| `requirements_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled` or `public` | | `requirements_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private`, `enabled` or `public` |

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -172,7 +172,7 @@ Add a variable name (key) and value here to override the value defined in
[the UI or `.gitlab-ci.yml`](../variables/README.md#custom-cicd-variables), [the UI or `.gitlab-ci.yml`](../variables/README.md#custom-cicd-variables),
for a single run of the manual job. for a single run of the manual job.
![Manual job variables](img/manual_job_variables.png) ![Manual job variables](img/manual_job_variables_v13_10.png)
## Delay a job ## Delay a job
@ -200,10 +200,10 @@ the duration.
In the following example: In the following example:
- Two sections are collapsed and can be expanded. - Three sections are collapsed and can be expanded.
- Three sections are expanded and can be collapsed. - Three sections are expanded and can be collapsed.
![Collapsible sections](img/collapsible_log_v12_6.png) ![Collapsible sections](img/collapsible_log_v13_10.png)
### Custom collapsible sections ### Custom collapsible sections

View File

@ -76,7 +76,7 @@ branch in the project.
GitLab CI/CD not only executes the jobs but also shows you what's happening during execution, GitLab CI/CD not only executes the jobs but also shows you what's happening during execution,
just as you would see in your terminal: just as you would see in your terminal:
![job running](img/job_running.png) ![job running](img/job_running_v13_10.png)
You create the strategy for your app and GitLab runs the pipeline You create the strategy for your app and GitLab runs the pipeline
according to what you've defined. Your pipeline status is also according to what you've defined. Your pipeline status is also

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

View File

@ -8,9 +8,8 @@ type: reference
# Continuous Integration and Deployment Admin settings **(FREE SELF)** # Continuous Integration and Deployment Admin settings **(FREE SELF)**
In this area, you will find settings for Auto DevOps, runners, and job artifacts. In this area, you will find settings for Auto DevOps, runners, and job artifacts.
You can find it in the **Admin Area > Settings > CI/CD**. You can find it in the [Admin Area](index.md) by navigating to
**Admin Area > Settings > CI/CD**.
![Admin Area settings button](../img/admin_area_settings_button.png)
## Auto DevOps **(FREE SELF)** ## Auto DevOps **(FREE SELF)**

View File

@ -133,18 +133,6 @@ The results are saved as a
that you can later download and analyze. Due to implementation limitations, we that you can later download and analyze. Due to implementation limitations, we
always take the latest Secret Detection artifact available. always take the latest Secret Detection artifact available.
### Post-processing
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4639) in GitLab 13.6.
Upon detection of a secret, GitLab supports post processing hooks. These can be used to take actions like notifying the cloud service who issued the secret. The cloud provider can confirm the credentials and take remediation actions like revoking or reissuing a new secret and notifying the creator of the secret. Post-processing workflows vary by supported cloud providers.
GitLab currently supports post-processing for following service providers:
- Amazon Web Services (AWS)
Third party cloud and SaaS providers can [express integration interest by filling out this form](https://forms.gle/wWpvrtLRK21Q2WJL9). Learn more about the [technical details of post-processing secrets](https://gitlab.com/groups/gitlab-org/-/epics/4639).
### Customizing settings ### Customizing settings
The Secret Detection scan settings can be changed through [CI/CD variables](#available-variables) The Secret Detection scan settings can be changed through [CI/CD variables](#available-variables)
@ -249,6 +237,34 @@ From highest to lowest severity, the logging levels are:
- `info` (default) - `info` (default)
- `debug` - `debug`
## Post-processing and revocation
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4639) in GitLab 13.6.
Upon detection of a secret, GitLab supports post-processing hooks. These can be used to take actions like notifying the cloud service who issued the secret. The cloud provider can confirm the credentials and take remediation actions like revoking or reissuing a new secret and notifying the creator of the secret. Post-processing workflows vary by supported cloud providers.
GitLab currently supports post-processing for following service providers:
- Amazon Web Services (AWS)
Third party cloud and SaaS providers can [express integration interest by filling out this form](https://forms.gle/wWpvrtLRK21Q2WJL9). Learn more about the [technical details of post-processing secrets](https://gitlab.com/groups/gitlab-org/-/epics/4639).
NOTE:
Post-processing is currently limited to a project's default branch, see the above epic for future efforts to support additional branches.
```mermaid
sequenceDiagram
autonumber
Rails->>+Sidekiq: gl-secret-detection-report.json
Sidekiq-->+Sidekiq: BuildFinishedWorker
Sidekiq-->+RevocationAPI: GET revocable keys types
RevocationAPI-->>-Sidekiq: OK
Sidekiq->>+RevocationAPI: POST revoke revocable keys
RevocationAPI-->>-Sidekiq: ACCEPTED
RevocationAPI-->>+Cloud Vendor: revoke revocable keys
Cloud Vendor-->>+RevocationAPI: ACCEPTED
```
## Full History Secret Scan ## Full History Secret Scan
GitLab 12.11 introduced support for scanning the full history of a repository. This new functionality GitLab 12.11 introduced support for scanning the full history of a repository. This new functionality

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -73,12 +73,13 @@ CSV file containing details of the resources scanned.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235558) in GitLab 13.6. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235558) in GitLab 13.6.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285476) in GitLab 13.10, options to zoom in on a date range, and download the vulnerabilities chart. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285476) in GitLab 13.10, options to zoom in on a date range, and download the vulnerabilities chart.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/285477) in GitLab 13.11, date range slider to visualise data between given dates.
At the project level, the Security Dashboard displays a chart with the number of vulnerabilities over time. At the project level, the Security Dashboard displays a chart with the number of vulnerabilities over time.
Access it by navigating to **Security & Compliance > Security Dashboard**. We display historical Access it by navigating to **Security & Compliance > Security Dashboard**. We display historical
data up to 365 days. The chart's data is updated daily. data up to 365 days. The chart's data is updated daily.
![Project Security Dashboard](img/project_security_dashboard_chart_v13_10.png) ![Project Security Dashboard](img/project_security_dashboard_chart_v13_11.png)
Filter the historical data by clicking on the corresponding legend name. The image above, for example, shows Filter the historical data by clicking on the corresponding legend name. The image above, for example, shows
only the graph for vulnerabilities with **high** severity. only the graph for vulnerabilities with **high** severity.

View File

@ -62,7 +62,7 @@ module API
authorize_upload!(project) authorize_upload!(project)
bad_request!('File is too large') if max_file_size_exceeded? bad_request!('File is too large') if max_file_size_exceeded?
track_event('push_package') ::Gitlab::Tracking.event(self.options[:for].name, 'push_package')
create_package_file_params = declared_params.merge(build: current_authenticated_job) create_package_file_params = declared_params.merge(build: current_authenticated_job)
::Packages::Generic::CreatePackageFileService ::Packages::Generic::CreatePackageFileService
@ -94,7 +94,7 @@ module API
package = ::Packages::Generic::PackageFinder.new(project).execute!(params[:package_name], params[:package_version]) package = ::Packages::Generic::PackageFinder.new(project).execute!(params[:package_name], params[:package_version])
package_file = ::Packages::PackageFileFinder.new(package, params[:file_name]).execute! package_file = ::Packages::PackageFileFinder.new(package, params[:file_name]).execute!
track_event('pull_package') ::Gitlab::Tracking.event(self.options[:for].name, 'pull_package')
present_carrierwave_file!(package_file.file) present_carrierwave_file!(package_file.file)
end end

View File

@ -544,17 +544,6 @@ module API
end end
end end
def track_event(action = action_name, **args)
category = args.delete(:category) || self.options[:for].name
raise "invalid category" unless category
::Gitlab::Tracking.event(category, action.to_s, **args)
rescue => error
Gitlab::AppLogger.warn(
"Tracking event failed for action: #{action}, category: #{category}, message: #{error.message}"
)
end
def increment_counter(event_name) def increment_counter(event_name)
feature_name = "usage_data_#{event_name}" feature_name = "usage_data_#{event_name}"
return unless Feature.enabled?(feature_name) return unless Feature.enabled?(feature_name)

View File

@ -10,7 +10,7 @@ module API
def redirect_registry_request(forward_to_registry, package_type, options) def redirect_registry_request(forward_to_registry, package_type, options)
if forward_to_registry && redirect_registry_request_available? if forward_to_registry && redirect_registry_request_available?
track_event("#{package_type}_request_forward") ::Gitlab::Tracking.event(self.options[:for].name, "#{package_type}_request_forward")
redirect(registry_url(package_type, options)) redirect(registry_url(package_type, options))
else else
yield yield

View File

@ -50,7 +50,8 @@ module API
def track_package_event(event_name, scope, **args) def track_package_event(event_name, scope, **args)
::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute ::Packages::CreateEventService.new(nil, current_user, event_name: event_name, scope: scope).execute
track_event(event_name, **args) category = args.delete(:category) || self.options[:for].name
::Gitlab::Tracking.event(category, event_name.to_s, **args)
end end
end end
end end

View File

@ -85,7 +85,6 @@ module Gitlab
id: runner.id, id: runner.id,
description: runner.description, description: runner.description,
active: runner.active?, active: runner.active?,
is_shared: runner.instance_type?,
tags: runner.tags&.map(&:name) tags: runner.tags&.map(&:name)
} }
end end

View File

@ -77,7 +77,6 @@ module Gitlab
id: runner.id, id: runner.id,
description: runner.description, description: runner.description,
active: runner.active?, active: runner.active?,
is_shared: runner.instance_type?,
tags: runner.tags&.map(&:name) tags: runner.tags&.map(&:name)
} }
end end

View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require 'magic'
# This wraps calls to a gem which support mime type detection.
# We use the `ruby-magic` gem instead of `mimemagic` due to licensing issues
module Gitlab
module Utils
class MimeType
class << self
def from_io(io)
return unless io.is_a?(IO) || io.is_a?(StringIO)
mime_type = File.magic(io, Magic::MIME_TYPE)
mime_type == 'inode/x-empty' ? nil : mime_type
end
def from_string(string)
return unless string.is_a?(String)
string.type
end
end
end
end
end

View File

@ -59,7 +59,7 @@
"@gitlab/favicon-overlay": "2.0.0", "@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.185.0", "@gitlab/svgs": "1.185.0",
"@gitlab/tributejs": "1.0.0", "@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "28.9.1", "@gitlab/ui": "28.15.0",
"@gitlab/visual-review-tools": "1.6.1", "@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "^6.0.3-4", "@rails/actioncable": "^6.0.3-4",
"@rails/ujs": "^6.0.3-4", "@rails/ujs": "^6.0.3-4",

View File

@ -333,6 +333,31 @@ RSpec.describe 'Merge request > User sees pipelines triggered by merge request',
end end
end end
context 'when the latest pipeline is running in the parent project' do
before do
Ci::CreatePipelineService.new(project, user, ref: 'feature')
.execute(:merge_request_event, merge_request: merge_request)
end
context 'when the previous pipeline failed in the fork project' do
before do
detached_merge_request_pipeline.drop!
end
context 'when the parent project enables pipeline must succeed' do
before do
project.update!(only_allow_merge_if_pipeline_succeeds: true)
end
it 'shows MWPS button' do
visit project_merge_request_path(project, merge_request)
expect(page).to have_button('Merge when pipeline succeeds')
end
end
end
end
context 'when a user merges a merge request from a forked project to the parent project' do context 'when a user merges a merge request from a forked project to the parent project' do
before do before do
click_link("Overview") click_link("Overview")

BIN
spec/fixtures/rails_sample.bmp vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

BIN
spec/fixtures/rails_sample.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
spec/fixtures/rails_sample.tif vendored Normal file

Binary file not shown.

BIN
spec/fixtures/sample.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -41,7 +41,6 @@ describe('feature_highlight/feature_highlight_popover', () => {
expect(findPopover().props()).toMatchObject({ expect(findPopover().props()).toMatchObject({
target: POPOVER_TARGET_ID, target: POPOVER_TARGET_ID,
cssClasses: ['feature-highlight-popover'], cssClasses: ['feature-highlight-popover'],
triggers: 'hover',
container: 'body', container: 'body',
placement: 'right', placement: 'right',
boundary: 'viewport', boundary: 'viewport',

View File

@ -52,7 +52,9 @@ exports[`packages_list_app renders 1`] = `
with GitLab. with GitLab.
</p> </p>
<div> <div
class="gl-display-flex gl-flex-wrap gl-justify-content-center"
>
<!----> <!---->
<!----> <!---->

View File

@ -10,8 +10,8 @@ exports[`EmptyStateComponent should render content 1`] = `
<h1 class=\\"h4\\">Getting started with serverless</h1> <h1 class=\\"h4\\">Getting started with serverless</h1>
<p>In order to start using functions as a service, you must first install Knative on your Kubernetes cluster. <gl-link-stub href=\\"/help\\">More information</gl-link-stub> <p>In order to start using functions as a service, you must first install Knative on your Kubernetes cluster. <gl-link-stub href=\\"/help\\">More information</gl-link-stub>
</p> </p>
<div> <div class=\\"gl-display-flex gl-flex-wrap gl-justify-content-center\\">
<gl-button-stub category=\\"primary\\" variant=\\"success\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" href=\\"/clusters\\">Install Knative</gl-button-stub> <gl-button-stub category=\\"primary\\" variant=\\"confirm\\" size=\\"medium\\" icon=\\"\\" buttontextclasses=\\"\\" href=\\"/clusters\\" class=\\"gl-mb-3 gl-mx-2\\">Install Knative</gl-button-stub>
<!----> <!---->
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@ import { joinPaths } from '~/lib/utils/url_utility';
import Tracking from '~/tracking'; import Tracking from '~/tracking';
import AlertDetails from '~/vue_shared/alert_details/components/alert_details.vue'; import AlertDetails from '~/vue_shared/alert_details/components/alert_details.vue';
import AlertSummaryRow from '~/vue_shared/alert_details/components/alert_summary_row.vue'; import AlertSummaryRow from '~/vue_shared/alert_details/components/alert_summary_row.vue';
import { SEVERITY_LEVELS } from '~/vue_shared/alert_details/constants'; import { PAGE_CONFIG, SEVERITY_LEVELS } from '~/vue_shared/alert_details/constants';
import createIssueMutation from '~/vue_shared/alert_details/graphql/mutations/alert_issue_create.mutation.graphql'; import createIssueMutation from '~/vue_shared/alert_details/graphql/mutations/alert_issue_create.mutation.graphql';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
import mockAlerts from './mocks/alerts.json'; import mockAlerts from './mocks/alerts.json';
@ -271,7 +271,13 @@ describe('AlertDetails', () => {
}); });
it('should display a table of raw alert details data', () => { it('should display a table of raw alert details data', () => {
expect(findDetailsTable().exists()).toBe(true); const details = findDetailsTable();
expect(details.exists()).toBe(true);
expect(details.props()).toStrictEqual({
alert: mockAlert,
statuses: PAGE_CONFIG.OPERATIONS.STATUSES,
loading: false,
});
}); });
}); });

View File

@ -12,6 +12,7 @@ describe('AlertManagementStatus', () => {
let wrapper; let wrapper;
const findStatusDropdown = () => wrapper.find(GlDropdown); const findStatusDropdown = () => wrapper.find(GlDropdown);
const findFirstStatusOption = () => findStatusDropdown().find(GlDropdownItem); const findFirstStatusOption = () => findStatusDropdown().find(GlDropdownItem);
const findAllStatusOptions = () => findStatusDropdown().findAll(GlDropdownItem);
const selectFirstStatusOption = () => { const selectFirstStatusOption = () => {
findFirstStatusOption().vm.$emit('click'); findFirstStatusOption().vm.$emit('click');
@ -131,6 +132,24 @@ describe('AlertManagementStatus', () => {
}); });
}); });
describe('Statuses', () => {
it('renders default translated statuses', () => {
mountComponent({});
expect(findAllStatusOptions().length).toBe(3);
expect(findFirstStatusOption().text()).toBe('Triggered');
});
it('renders translated statuses', () => {
const status = 'TEST';
const translatedStatus = 'Test';
mountComponent({
props: { alert: { ...mockAlert, status }, statuses: { [status]: translatedStatus } },
});
expect(findAllStatusOptions().length).toBe(1);
expect(findFirstStatusOption().text()).toBe(translatedStatus);
});
});
describe('Snowplow tracking', () => { describe('Snowplow tracking', () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(Tracking, 'event'); jest.spyOn(Tracking, 'event');

View File

@ -76,20 +76,4 @@ describe('Alert Details Sidebar', () => {
expect(wrapper.find(SidebarStatus).exists()).toBe(true); expect(wrapper.find(SidebarStatus).exists()).toBe(true);
}); });
}); });
describe('the sidebar renders for threat monitoring', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mountComponent();
});
it('should not render side bar status dropdown', () => {
mountComponent({
mountMethod: mount,
alert: mockAlert,
provide: { isThreatMonitoringPage: true },
});
expect(wrapper.find(SidebarStatus).exists()).toBe(false);
});
});
}); });

View File

@ -1,7 +1,9 @@
import { GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui'; import { GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import updateAlertStatusMutation from '~/graphql_shared/mutations/alert_status_update.mutation.graphql'; import updateAlertStatusMutation from '~/graphql_shared/mutations/alert_status_update.mutation.graphql';
import AlertStatus from '~/vue_shared/alert_details/components/alert_status.vue';
import AlertSidebarStatus from '~/vue_shared/alert_details/components/sidebar/sidebar_status.vue'; import AlertSidebarStatus from '~/vue_shared/alert_details/components/sidebar/sidebar_status.vue';
import { PAGE_CONFIG } from '~/vue_shared/alert_details/constants';
import mockAlerts from '../mocks/alerts.json'; import mockAlerts from '../mocks/alerts.json';
const mockAlert = mockAlerts[0]; const mockAlert = mockAlerts[0];
@ -12,8 +14,16 @@ describe('Alert Details Sidebar Status', () => {
const findStatusDropdownItem = () => wrapper.find(GlDropdownItem); const findStatusDropdownItem = () => wrapper.find(GlDropdownItem);
const findStatusLoadingIcon = () => wrapper.find(GlLoadingIcon); const findStatusLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findStatusDropdownHeader = () => wrapper.find('[data-testid="dropdown-header"]'); const findStatusDropdownHeader = () => wrapper.find('[data-testid="dropdown-header"]');
const findAlertStatus = () => wrapper.findComponent(AlertStatus);
const findStatus = () => wrapper.find('[data-testid="status"]');
function mountComponent({ data, sidebarCollapsed = true, loading = false, stubs = {} } = {}) { function mountComponent({
data,
sidebarCollapsed = true,
loading = false,
stubs = {},
provide = {},
} = {}) {
wrapper = mount(AlertSidebarStatus, { wrapper = mount(AlertSidebarStatus, {
propsData: { propsData: {
alert: { ...mockAlert }, alert: { ...mockAlert },
@ -32,6 +42,7 @@ describe('Alert Details Sidebar Status', () => {
}, },
}, },
stubs, stubs,
provide,
}); });
} }
@ -96,8 +107,24 @@ describe('Alert Details Sidebar Status', () => {
jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error())); jest.spyOn(wrapper.vm.$apollo, 'mutate').mockReturnValue(Promise.reject(new Error()));
findStatusDropdownItem().vm.$emit('click'); findStatusDropdownItem().vm.$emit('click');
expect(findStatusLoadingIcon().exists()).toBe(false); expect(findStatusLoadingIcon().exists()).toBe(false);
expect(wrapper.find('[data-testid="status"]').text()).toBe('Triggered'); expect(findStatus().text()).toBe('Triggered');
}); });
}); });
}); });
describe('Statuses', () => {
it('renders default translated statuses', () => {
mountComponent({});
expect(findAlertStatus().props('statuses')).toBe(PAGE_CONFIG.OPERATIONS.STATUSES);
expect(findStatus().text()).toBe('Triggered');
});
it('renders translated statuses', () => {
const status = 'TEST';
const statuses = { [status]: 'Test' };
mountComponent({ data: { alert: { ...mockAlert, status } }, provide: { statuses } });
expect(findAlertStatus().props('statuses')).toBe(statuses);
expect(findStatus().text()).toBe(statuses.TEST);
});
});
}); });

View File

@ -75,45 +75,62 @@ describe('AlertDetails', () => {
}); });
describe('with table data', () => { describe('with table data', () => {
beforeEach(mountComponent); describe('default', () => {
beforeEach(mountComponent);
it('renders a table', () => { it('renders a table', () => {
expect(findTableComponent().exists()).toBe(true); expect(findTableComponent().exists()).toBe(true);
});
it('renders a cell based on alert data', () => {
expect(findTableComponent().text()).toContain('SyntaxError: Invalid or unexpected token');
});
it('should show allowed alert fields', () => {
const fields = findTableKeys();
['Iid', 'Title', 'Severity', 'Status', 'Hosts', 'Environment'].forEach((field) => {
expect(findTableField(fields, field).exists()).toBe(true);
});
});
it('should not show disallowed alert fields', () => {
const fields = findTableKeys();
['Typename', 'Todos', 'Notes', 'Assignees'].forEach((field) => {
expect(findTableField(fields, field).exists()).toBe(false);
});
});
}); });
it('renders a cell based on alert data', () => { describe('environment', () => {
expect(findTableComponent().text()).toContain('SyntaxError: Invalid or unexpected token'); it('should display only the name for the environment', () => {
mountComponent();
expect(findTableFieldValueByKey('Environment').text()).toBe(environmentName);
});
it('should not display the environment row if there is not data', () => {
environmentData = { name: null, path: null };
mountComponent();
expect(findTableFieldValueByKey('Environment').text()).toBeFalsy();
});
}); });
it('should show allowed alert fields', () => { describe('status', () => {
const fields = findTableKeys(); it('should show the translated status for the default statuses', () => {
mountComponent();
expect(findTableFieldValueByKey('Status').text()).toBe('Triggered');
});
expect(findTableField(fields, 'Iid').exists()).toBe(true); it('should show the translated status for provided statuses', () => {
expect(findTableField(fields, 'Title').exists()).toBe(true); const translatedStatus = 'Test';
expect(findTableField(fields, 'Severity').exists()).toBe(true); mountComponent({ statuses: { TRIGGERED: translatedStatus } });
expect(findTableField(fields, 'Status').exists()).toBe(true); expect(findTableFieldValueByKey('Status').text()).toBe(translatedStatus);
expect(findTableField(fields, 'Hosts').exists()).toBe(true); });
expect(findTableField(fields, 'Environment').exists()).toBe(true);
});
it('should not show disallowed alert fields', () => { it('should show the provided status if value is not defined in statuses', () => {
const fields = findTableKeys(); mountComponent({ statuses: {} });
expect(findTableFieldValueByKey('Status').text()).toBe('TRIGGERED');
expect(findTableField(fields, 'Typename').exists()).toBe(false); });
expect(findTableField(fields, 'Todos').exists()).toBe(false);
expect(findTableField(fields, 'Notes').exists()).toBe(false);
expect(findTableField(fields, 'Assignees').exists()).toBe(false);
});
it('should display only the name for the environment', () => {
expect(findTableFieldValueByKey('Environment').text()).toBe(environmentName);
});
it('should not display the environment row if there is not data', () => {
environmentData = { name: null, path: null };
mountComponent();
expect(findTableFieldValueByKey('Environment').text()).toBeFalsy();
}); });
}); });
}); });

View File

@ -41,10 +41,6 @@ describe('HelpPopover', () => {
expect(findPopover().props().target()).toBe(findQuestionButton().vm.$el); expect(findPopover().props().target()).toBe(findQuestionButton().vm.$el);
}); });
it('triggers popover on hover and focus', () => {
expect(findPopover().props().triggers).toBe('hover focus');
});
it('allows rendering title with HTML tags', () => { it('allows rendering title with HTML tags', () => {
expect(findPopover().find('strong').exists()).toBe(true); expect(findPopover().find('strong').exists()).toBe(true);
}); });

View File

@ -12,6 +12,10 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
subject { helper.redirect_registry_request(forward_to_registry, package_type, options) { helper.fallback } } subject { helper.redirect_registry_request(forward_to_registry, package_type, options) { helper.fallback } }
before do
allow(helper).to receive(:options).and_return(for: API::NpmInstancePackages)
end
shared_examples 'executing fallback' do shared_examples 'executing fallback' do
it 'redirects to package registry' do it 'redirects to package registry' do
expect(helper).to receive(:registry_url).never expect(helper).to receive(:registry_url).never
@ -23,13 +27,14 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
end end
shared_examples 'executing redirect' do shared_examples 'executing redirect' do
it 'redirects to package registry' do it 'redirects to package registry', :snowplow do
expect(helper).to receive(:track_event).with('npm_request_forward').once
expect(helper).to receive(:registry_url).once expect(helper).to receive(:registry_url).once
expect(helper).to receive(:redirect).once expect(helper).to receive(:redirect).once
expect(helper).to receive(:fallback).never expect(helper).to receive(:fallback).never
subject subject
expect_snowplow_event(category: 'API::NpmInstancePackages', action: 'npm_request_forward')
end end
end end
@ -64,7 +69,6 @@ RSpec.describe API::Helpers::Packages::DependencyProxyHelpers do
let(:package_type) { pkg_type } let(:package_type) { pkg_type }
it 'raises an error' do it 'raises an error' do
allow(helper).to receive(:track_event)
expect { subject }.to raise_error(ArgumentError, "Can't build registry_url for package_type #{package_type}") expect { subject }.to raise_error(ArgumentError, "Can't build registry_url for package_type #{package_type}")
end end
end end

View File

@ -175,20 +175,6 @@ RSpec.describe API::Helpers do
end end
end end
describe '#track_event' do
it "creates a gitlab tracking event", :snowplow do
subject.track_event('my_event', category: 'foo')
expect_snowplow_event(category: 'foo', action: 'my_event')
end
it "logs an exception" do
expect(Gitlab::AppLogger).to receive(:warn).with(/Tracking event failed/)
subject.track_event('my_event', category: nil)
end
end
describe '#increment_unique_values' do describe '#increment_unique_values' do
let(:value) { '9f302fea-f828-4ca9-aef4-e10bd723c0b3' } let(:value) { '9f302fea-f828-4ca9-aef4-e10bd723c0b3' }
let(:event_name) { 'g_compliance_dashboard' } let(:event_name) { 'g_compliance_dashboard' }

View File

@ -59,7 +59,6 @@ RSpec.describe Gitlab::DataBuilder::Pipeline do
expect(runner_data[:id]).to eq(ci_runner.id) expect(runner_data[:id]).to eq(ci_runner.id)
expect(runner_data[:description]).to eq(ci_runner.description) expect(runner_data[:description]).to eq(ci_runner.description)
expect(runner_data[:active]).to eq(ci_runner.active) expect(runner_data[:active]).to eq(ci_runner.active)
expect(runner_data[:is_shared]).to eq(ci_runner.instance_type?)
expect(runner_data[:tags]).to match_array(tag_names) expect(runner_data[:tags]).to match_array(tag_names)
end end
end end

View File

@ -4,7 +4,7 @@ require 'spec_helper'
RSpec.describe Gitlab::SQL::CTE do RSpec.describe Gitlab::SQL::CTE do
describe '#to_arel' do describe '#to_arel' do
it 'generates an Arel relation for the CTE body' do it 'generates an Arel relation for the CTE body', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/325916' do
relation = User.where(id: 1) relation = User.where(id: 1)
cte = described_class.new(:cte_name, relation) cte = described_class.new(:cte_name, relation)
sql = cte.to_arel.to_sql sql = cte.to_arel.to_sql

View File

@ -0,0 +1,57 @@
# frozen_string_literal: true
require "fast_spec_helper"
require "rspec/parameterized"
RSpec.describe Gitlab::Utils::MimeType do
describe ".from_io" do
subject { described_class.from_io(io) }
context "input isn't an IO" do
let(:io) { "test" }
it "returns nil" do
expect(subject).to be_nil
end
end
context "input is a file" do
using RSpec::Parameterized::TableSyntax
where(:fixture, :mime_type) do
"banana_sample.gif" | "image/gif"
"rails_sample.jpg" | "image/jpeg"
"rails_sample.png" | "image/png"
"rails_sample.bmp" | "image/bmp"
"rails_sample.tif" | "image/tiff"
"sample.ico" | "image/vnd.microsoft.icon"
"blockquote_fence_before.md" | "text/plain"
"csv_empty.csv" | "application/x-empty"
end
with_them do
let(:io) { File.open(File.join(__dir__, "../../../fixtures", fixture)) }
it { is_expected.to eq(mime_type) }
end
end
end
describe ".from_string" do
subject { described_class.from_string(str) }
context "input isn't a string" do
let(:str) { nil }
it "returns nil" do
expect(subject).to be_nil
end
end
context "input is a string" do
let(:str) { "plain text" }
it { is_expected.to eq('text/plain') }
end
end
end

View File

@ -13,7 +13,6 @@ end
# @param mime_type [String] mime type to forcibly detect. # @param mime_type [String] mime type to forcibly detect.
RSpec.shared_context 'force content type detection to mime_type' do RSpec.shared_context 'force content type detection to mime_type' do
before do before do
magic_mime_obj = MimeMagic.new(mime_type) allow(Gitlab::Utils::MimeType).to receive(:from_io).and_return(mime_type)
allow(MimeMagic).to receive(:by_magic).with(anything).and_return(magic_mime_obj)
end end
end end

View File

@ -907,16 +907,16 @@
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8" resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw== integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
"@gitlab/ui@28.9.1": "@gitlab/ui@28.15.0":
version "28.9.1" version "28.15.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-28.9.1.tgz#7d4d4502ff09fca19ab815504f80afbf03dd2fc1" resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-28.15.0.tgz#e78a1c0724c7cc8880fcff8161e529ca7bcaf6e8"
integrity sha512-+JqkpwzkKBnxo4KkC8XSPEJ5Au9y+TIOE7w9I5o+04krgWCbZKNqaiKZkg2IqSlo/sZSfvihXZMhEVc/JXf7HQ== integrity sha512-muz1tX3nQmu9dMv7GbTNIkWkEwYhvnLPhtwtnrt8eyRGQ0zIUWLEzdoSiwvMNLAqT2JB8kxahoavR5iSFAYtXA==
dependencies: dependencies:
"@babel/standalone" "^7.0.0" "@babel/standalone" "^7.0.0"
"@gitlab/vue-toasted" "^1.3.0" "@gitlab/vue-toasted" "^1.3.0"
bootstrap-vue "2.13.1" bootstrap-vue "2.13.1"
copy-to-clipboard "^3.0.8" copy-to-clipboard "^3.0.8"
dompurify "^2.2.6" dompurify "^2.2.7"
echarts "^4.9.0" echarts "^4.9.0"
highlight.js "^10.6.0" highlight.js "^10.6.0"
js-beautify "^1.8.8" js-beautify "^1.8.8"
@ -4270,7 +4270,7 @@ domhandler@^2.3.0:
dependencies: dependencies:
domelementtype "1" domelementtype "1"
dompurify@^2.2.6, dompurify@^2.2.7: dompurify@^2.2.7:
version "2.2.7" version "2.2.7"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.2.7.tgz#a5f055a2a471638680e779bd08fc334962d11fd8" resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.2.7.tgz#a5f055a2a471638680e779bd08fc334962d11fd8"
integrity sha512-jdtDffdGNY+C76jvodNTu9jt5yYj59vuTUyx+wXdzcSwAGTYZDAQkQ7Iwx9zcGrA4ixC1syU4H3RZROqRxokxg== integrity sha512-jdtDffdGNY+C76jvodNTu9jt5yYj59vuTUyx+wXdzcSwAGTYZDAQkQ7Iwx9zcGrA4ixC1syU4H3RZROqRxokxg==