Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
705210af74
commit
cd49d91db3
26 changed files with 574 additions and 286 deletions
|
@ -127,8 +127,12 @@ export default {
|
|||
</p>
|
||||
<gl-progress-bar :value="progressValue" :max="$options.maxValue" />
|
||||
</div>
|
||||
<div class="row row-cols-1 row-cols-md-3 gl-mt-5">
|
||||
<div v-for="section in $options.actionSections" :key="section" class="col gl-mb-6">
|
||||
<div class="row">
|
||||
<div
|
||||
v-for="section in $options.actionSections"
|
||||
:key="section"
|
||||
class="gl-mt-5 col-sm-12 col-mb-6 col-lg-4"
|
||||
>
|
||||
<learn-gitlab-section-card
|
||||
:section="section"
|
||||
:svg="svgFor(section)"
|
||||
|
|
|
@ -34,17 +34,23 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<gl-card class="gl-pt-0 learn-gitlab-section-card">
|
||||
<div class="learn-gitlab-section-card-header">
|
||||
<gl-card
|
||||
class="gl-pt-0 h-100"
|
||||
header-class="gl-bg-white gl-border-0 gl-pb-0"
|
||||
body-class="gl-pt-0"
|
||||
>
|
||||
<template #header>
|
||||
<img :src="svg" />
|
||||
<h2 class="gl-font-lg gl-mb-3">{{ $options.i18n[section].title }}</h2>
|
||||
<p class="gl-text-gray-700 gl-mb-6">{{ $options.i18n[section].description }}</p>
|
||||
</div>
|
||||
<learn-gitlab-section-link
|
||||
v-for="[action, value] in sortedActions"
|
||||
:key="action"
|
||||
:action="action"
|
||||
:value="value"
|
||||
/>
|
||||
</template>
|
||||
<template #default>
|
||||
<learn-gitlab-section-link
|
||||
v-for="[action, value] in sortedActions"
|
||||
:key="action"
|
||||
:action="action"
|
||||
:value="value"
|
||||
/>
|
||||
</template>
|
||||
</gl-card>
|
||||
</template>
|
||||
|
|
|
@ -1,16 +1,25 @@
|
|||
<script>
|
||||
import { GlLink, GlIcon } from '@gitlab/ui';
|
||||
import { GlLink, GlIcon, GlButton, GlTooltipDirective as GlTooltip } from '@gitlab/ui';
|
||||
import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue';
|
||||
import { isExperimentVariant } from '~/experimentation/utils';
|
||||
import eventHub from '~/invite_members/event_hub';
|
||||
import { s__ } from '~/locale';
|
||||
import { s__, __ } from '~/locale';
|
||||
import { ACTION_LABELS } from '../constants';
|
||||
|
||||
export default {
|
||||
name: 'LearnGitlabSectionLink',
|
||||
components: { GlLink, GlIcon },
|
||||
components: {
|
||||
GlLink,
|
||||
GlIcon,
|
||||
GlButton,
|
||||
GitlabExperiment,
|
||||
},
|
||||
directives: {
|
||||
GlTooltip,
|
||||
},
|
||||
i18n: {
|
||||
ACTION_LABELS,
|
||||
trialOnly: s__('LearnGitlab|Trial only'),
|
||||
watchHow: __('Watch how'),
|
||||
},
|
||||
props: {
|
||||
action: {
|
||||
|
@ -23,6 +32,9 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
linkTitle() {
|
||||
return ACTION_LABELS[this.action].title;
|
||||
},
|
||||
trialOnly() {
|
||||
return ACTION_LABELS[this.action].trialRequired;
|
||||
},
|
||||
|
@ -34,6 +46,9 @@ export default {
|
|||
openInNewTab() {
|
||||
return ACTION_LABELS[this.action]?.openInNewTab === true || this.value.openInNewTab === true;
|
||||
},
|
||||
linkToVideoTutorial() {
|
||||
return ACTION_LABELS[this.action].videoTutorial;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
openModal() {
|
||||
|
@ -44,32 +59,54 @@ export default {
|
|||
</script>
|
||||
<template>
|
||||
<div class="gl-mb-4">
|
||||
<span v-if="value.completed" class="gl-text-green-500">
|
||||
<gl-icon name="check-circle-filled" :size="16" data-testid="completed-icon" />
|
||||
{{ $options.i18n.ACTION_LABELS[action].title }}
|
||||
</span>
|
||||
<gl-link
|
||||
v-else-if="showInviteModalLink"
|
||||
data-track-action="click_link"
|
||||
:data-track-label="$options.i18n.ACTION_LABELS[action].title"
|
||||
data-track-property="Growth::Activation::Experiment::InviteForHelpContinuousOnboarding"
|
||||
data-testid="invite-for-help-continuous-onboarding-experiment-link"
|
||||
@click="openModal"
|
||||
>
|
||||
{{ $options.i18n.ACTION_LABELS[action].title }}
|
||||
</gl-link>
|
||||
<gl-link
|
||||
v-else
|
||||
:target="openInNewTab ? '_blank' : '_self'"
|
||||
:href="value.url"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
:data-track-label="$options.i18n.ACTION_LABELS[action].title"
|
||||
>
|
||||
{{ $options.i18n.ACTION_LABELS[action].title }}
|
||||
</gl-link>
|
||||
<span v-if="trialOnly" class="gl-font-style-italic gl-text-gray-500" data-testid="trial-only">
|
||||
- {{ $options.i18n.trialOnly }}
|
||||
</span>
|
||||
<div v-if="trialOnly" class="gl-font-style-italic gl-text-gray-500" data-testid="trial-only">
|
||||
{{ $options.i18n.trialOnly }}
|
||||
</div>
|
||||
<div class="flex align-items-center">
|
||||
<span v-if="value.completed" class="gl-text-green-500">
|
||||
<gl-icon name="check-circle-filled" :size="16" data-testid="completed-icon" />
|
||||
{{ linkTitle }}
|
||||
</span>
|
||||
<gl-link
|
||||
v-else-if="showInviteModalLink"
|
||||
data-track-action="click_link"
|
||||
:data-track-label="linkTitle"
|
||||
data-track-property="Growth::Activation::Experiment::InviteForHelpContinuousOnboarding"
|
||||
data-testid="invite-for-help-continuous-onboarding-experiment-link"
|
||||
@click="openModal"
|
||||
>
|
||||
{{ linkTitle }}
|
||||
</gl-link>
|
||||
<gl-link
|
||||
v-else
|
||||
:target="openInNewTab ? '_blank' : '_self'"
|
||||
:href="value.url"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
:data-track-label="linkTitle"
|
||||
>
|
||||
{{ linkTitle }}
|
||||
</gl-link>
|
||||
<gitlab-experiment name="video_tutorials_continuous_onboarding">
|
||||
<template #control></template>
|
||||
<template #candidate>
|
||||
<gl-button
|
||||
v-if="linkToVideoTutorial"
|
||||
v-gl-tooltip
|
||||
category="tertiary"
|
||||
icon="live-preview"
|
||||
:title="$options.i18n.watchHow"
|
||||
:aria-label="$options.i18n.watchHow"
|
||||
:href="linkToVideoTutorial"
|
||||
target="_blank"
|
||||
class="ml-auto"
|
||||
data-testid="video-tutorial-link"
|
||||
data-track-action="click_video_link"
|
||||
:data-track-label="linkTitle"
|
||||
data-track-property="Growth::Conversion::Experiment::LearnGitLab"
|
||||
/>
|
||||
</template>
|
||||
</gitlab-experiment>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -40,6 +40,7 @@ export const ACTION_LABELS = {
|
|||
trialRequired: true,
|
||||
section: 'workspace',
|
||||
position: 4,
|
||||
videoTutorial: 'https://vimeo.com/670896787',
|
||||
},
|
||||
requiredMrApprovalsEnabled: {
|
||||
title: s__('LearnGitLab|Add merge request approval'),
|
||||
|
@ -48,6 +49,7 @@ export const ACTION_LABELS = {
|
|||
trialRequired: true,
|
||||
section: 'workspace',
|
||||
position: 5,
|
||||
videoTutorial: 'https://vimeo.com/670904904',
|
||||
},
|
||||
mergeRequestCreated: {
|
||||
title: s__('LearnGitLab|Submit a merge request'),
|
||||
|
|
|
@ -1,11 +1,3 @@
|
|||
.learn-gitlab-info-card-content {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.learn-gitlab-section-card {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.learn-gitlab-section-card-header {
|
||||
height: 165px;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ class Projects::LearnGitlabController < Projects::ApplicationController
|
|||
before_action :authenticate_user!
|
||||
before_action :check_experiment_enabled?
|
||||
before_action :enable_invite_for_help_continuous_onboarding_experiment
|
||||
before_action :enable_video_tutorials_continuous_onboarding_experiment
|
||||
|
||||
feature_category :users
|
||||
|
||||
|
@ -24,4 +25,8 @@ class Projects::LearnGitlabController < Projects::ApplicationController
|
|||
e.publish_to_database
|
||||
end
|
||||
end
|
||||
|
||||
def enable_video_tutorials_continuous_onboarding_experiment
|
||||
experiment(:video_tutorials_continuous_onboarding, namespace: project&.namespace).publish
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class VideoTutorialsContinuousOnboardingExperiment < ApplicationExperiment
|
||||
control { }
|
||||
candidate { }
|
||||
end
|
|
@ -14,6 +14,15 @@ module Mutations
|
|||
null: true,
|
||||
description: 'User preferences after mutation.'
|
||||
|
||||
def ready?(**args)
|
||||
if disabled_sort_value?(args)
|
||||
raise Gitlab::Graphql::Errors::ArgumentError,
|
||||
'Feature flag `incident_escalations` must be enabled to use this sort order.'
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def resolve(**attributes)
|
||||
user_preferences = current_user.user_preference
|
||||
user_preferences.update(attributes)
|
||||
|
@ -23,6 +32,14 @@ module Mutations
|
|||
errors: errors_on_object(user_preferences)
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def disabled_sort_value?(args)
|
||||
return false unless [:escalation_status_asc, :escalation_status_desc].include?(args[:issues_sort])
|
||||
|
||||
Feature.disabled?(:incident_escalations)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,8 @@ module Resolvers
|
|||
NON_STABLE_CURSOR_SORTS = %i[priority_asc priority_desc
|
||||
popularity_asc popularity_desc
|
||||
label_priority_asc label_priority_desc
|
||||
milestone_due_asc milestone_due_desc].freeze
|
||||
milestone_due_asc milestone_due_desc
|
||||
escalation_status_asc escalation_status_desc].freeze
|
||||
|
||||
def continue_issue_resolve(parent, finder, **args)
|
||||
issues = Gitlab::Graphql::Loaders::IssuableLoader.new(parent, finder).batching_find_all { |q| apply_lookahead(q) }
|
||||
|
@ -31,6 +32,13 @@ module Resolvers
|
|||
end
|
||||
end
|
||||
|
||||
def prepare_params(args, parent)
|
||||
return unless [:escalation_status_asc, :escalation_status_desc].include?(args[:sort])
|
||||
return if Feature.enabled?(:incident_escalations, parent)
|
||||
|
||||
args[:sort] = :created_desc # default for sort argument
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def unconditional_includes
|
||||
|
|
|
@ -84,6 +84,7 @@ module IssueResolverArguments
|
|||
|
||||
prepare_assignee_username_params(args)
|
||||
prepare_release_tag_params(args)
|
||||
prepare_params(args, parent) if defined?(prepare_params)
|
||||
|
||||
finder = IssuesFinder.new(current_user, args)
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ module Types
|
|||
value 'TITLE_DESC', 'Title by descending order.', value: :title_desc
|
||||
value 'POPULARITY_ASC', 'Number of upvotes (awarded "thumbs up" emoji) by ascending order.', value: :popularity_asc
|
||||
value 'POPULARITY_DESC', 'Number of upvotes (awarded "thumbs up" emoji) by descending order.', value: :popularity_desc
|
||||
value 'ESCALATION_STATUS_ASC', 'Status from triggered to resolved. Defaults to `CREATED_DESC` if `incident_escalations` feature flag is disabled.', value: :escalation_status_asc
|
||||
value 'ESCALATION_STATUS_DESC', 'Status from resolved to triggered. Defaults to `CREATED_DESC` if `incident_escalations` feature flag is disabled.', value: :escalation_status_desc
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -125,6 +125,8 @@ class Issue < ApplicationRecord
|
|||
scope :order_created_at_desc, -> { reorder(created_at: :desc) }
|
||||
scope :order_severity_asc, -> { includes(:issuable_severity).order('issuable_severities.severity ASC NULLS FIRST') }
|
||||
scope :order_severity_desc, -> { includes(:issuable_severity).order('issuable_severities.severity DESC NULLS LAST') }
|
||||
scope :order_escalation_status_asc, -> { includes(:incident_management_issuable_escalation_status).order(::Gitlab::Database.nulls_last_order('incident_management_issuable_escalation_status.status')) }
|
||||
scope :order_escalation_status_desc, -> { includes(:incident_management_issuable_escalation_status).order(::Gitlab::Database.nulls_last_order('incident_management_issuable_escalation_status.status', 'DESC')) }
|
||||
|
||||
scope :preload_associated_models, -> { preload(:assignees, :labels, project: :namespace) }
|
||||
scope :with_web_entity_associations, -> { preload(:author, project: [:project_feature, :route, namespace: :route]) }
|
||||
|
@ -327,6 +329,8 @@ class Issue < ApplicationRecord
|
|||
when 'relative_position', 'relative_position_asc' then order_by_relative_position
|
||||
when 'severity_asc' then order_severity_asc.with_order_id_desc
|
||||
when 'severity_desc' then order_severity_desc.with_order_id_desc
|
||||
when 'escalation_status_asc' then order_escalation_status_asc.with_order_id_desc
|
||||
when 'escalation_status_desc' then order_escalation_status_desc.with_order_id_desc
|
||||
else
|
||||
super
|
||||
end
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: video_tutorials_continuous_onboarding
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/82274
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/351916
|
||||
milestone: '14.9'
|
||||
type: experiment
|
||||
group: group::adoption
|
||||
default_enabled: false
|
|
@ -18298,6 +18298,8 @@ Values for sorting issues.
|
|||
| <a id="issuesortcreated_desc"></a>`CREATED_DESC` | Created at descending order. |
|
||||
| <a id="issuesortdue_date_asc"></a>`DUE_DATE_ASC` | Due date by ascending order. |
|
||||
| <a id="issuesortdue_date_desc"></a>`DUE_DATE_DESC` | Due date by descending order. |
|
||||
| <a id="issuesortescalation_status_asc"></a>`ESCALATION_STATUS_ASC` | Status from triggered to resolved. Defaults to `CREATED_DESC` if `incident_escalations` feature flag is disabled. |
|
||||
| <a id="issuesortescalation_status_desc"></a>`ESCALATION_STATUS_DESC` | Status from resolved to triggered. Defaults to `CREATED_DESC` if `incident_escalations` feature flag is disabled. |
|
||||
| <a id="issuesortlabel_priority_asc"></a>`LABEL_PRIORITY_ASC` | Label priority by ascending order. |
|
||||
| <a id="issuesortlabel_priority_desc"></a>`LABEL_PRIORITY_DESC` | Label priority by descending order. |
|
||||
| <a id="issuesortmilestone_due_asc"></a>`MILESTONE_DUE_ASC` | Milestone due date by ascending order. |
|
||||
|
|
|
@ -255,7 +255,12 @@ Add a to-do for incidents that you want to track in your to-do list. Click the
|
|||
|
||||
### Change incident status
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9.
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `incident_escalations`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
By default this feature is not available. To make it available per project or for your entire
|
||||
instance, ask an administrator to [enable the feature flag](../../administration/feature_flags.md)
|
||||
named `incident_escalations`.
|
||||
|
||||
For users with the Developer role or higher, select **Edit** in the **Status** section of the
|
||||
right-hand side bar of an incident, then select a status. **Triggered** is the default status for
|
||||
|
@ -273,7 +278,12 @@ updating the incident status also updates the alert status.
|
|||
|
||||
## Change escalation policy **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9.
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `incident_escalations`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
By default this feature is not available. To make it available per project or for your entire
|
||||
instance, ask an administrator to [enable the feature flag](../../administration/feature_flags.md)
|
||||
named `incident_escalations`.
|
||||
|
||||
For users with the Developer role or higher, select **Edit** in the **Escalation policy** section of
|
||||
the right-hand side bar of an incident, then select a policy. By default, new incidents do not have
|
||||
|
|
|
@ -50,7 +50,12 @@ or stop alert escalations by [updating the alert's status](alerts.md#update-an-a
|
|||
|
||||
### Escalating an incident
|
||||
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9.
|
||||
> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/5716) in GitLab 14.9 [with a flag](../../administration/feature_flags.md) named `incident_escalations`. Disabled by default.
|
||||
|
||||
FLAG:
|
||||
By default this feature is not available. To make it available per project or for your entire
|
||||
instance, ask an administrator to [enable the feature flag](../../administration/feature_flags.md)
|
||||
named `incident_escalations`.
|
||||
|
||||
For incidents, paging on-call responders is optional for each individual incident.
|
||||
To begin escalating the incident, [set the incident's escalation policy](incidents.md#change-escalation-policy).
|
||||
|
|
|
@ -203,7 +203,7 @@ The `assignee` and `assignee_id` keys are deprecated
|
|||
and contain the first assignee only.
|
||||
|
||||
The `escalation_status` and `escalation_policy` fields are
|
||||
only available for issue types which support escalations,
|
||||
only available for issue types which [support escalations](../../../operations/incident_management/paging.md#paging),
|
||||
such as incidents.
|
||||
|
||||
Request header:
|
||||
|
|
|
@ -41559,6 +41559,9 @@ msgstr ""
|
|||
msgid "Warning: Synchronizing LDAP removes direct members' access."
|
||||
msgstr ""
|
||||
|
||||
msgid "Watch how"
|
||||
msgstr ""
|
||||
|
||||
msgid "We are currently unable to fetch data for the pipeline header."
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe VideoTutorialsContinuousOnboardingExperiment do
|
||||
it "defines a control and candidate" do
|
||||
expect(subject.behaviors.keys).to match_array(%w[control candidate])
|
||||
end
|
||||
end
|
|
@ -2,31 +2,26 @@
|
|||
|
||||
exports[`Learn GitLab Section Card renders correctly 1`] = `
|
||||
<gl-card-stub
|
||||
bodyclass=""
|
||||
class="gl-pt-0 learn-gitlab-section-card"
|
||||
bodyclass="gl-pt-0"
|
||||
class="gl-pt-0 h-100"
|
||||
footerclass=""
|
||||
headerclass=""
|
||||
headerclass="gl-bg-white gl-border-0 gl-pb-0"
|
||||
>
|
||||
<div
|
||||
class="learn-gitlab-section-card-header"
|
||||
>
|
||||
<img
|
||||
src="workspace.svg"
|
||||
/>
|
||||
|
||||
<h2
|
||||
class="gl-font-lg gl-mb-3"
|
||||
>
|
||||
Set up your workspace
|
||||
</h2>
|
||||
|
||||
<p
|
||||
class="gl-text-gray-700 gl-mb-6"
|
||||
>
|
||||
Complete these tasks first so you can enjoy GitLab's features to their fullest:
|
||||
</p>
|
||||
</div>
|
||||
<img
|
||||
src="workspace.svg"
|
||||
/>
|
||||
|
||||
<h2
|
||||
class="gl-font-lg gl-mb-3"
|
||||
>
|
||||
Set up your workspace
|
||||
</h2>
|
||||
|
||||
<p
|
||||
class="gl-text-gray-700 gl-mb-6"
|
||||
>
|
||||
Complete these tasks first so you can enjoy GitLab's features to their fullest:
|
||||
</p>
|
||||
<learn-gitlab-section-link-stub
|
||||
action="userAdded"
|
||||
value="[object Object]"
|
||||
|
|
|
@ -51,170 +51,204 @@ exports[`Learn GitLab renders correctly 1`] = `
|
|||
</div>
|
||||
|
||||
<div
|
||||
class="row row-cols-1 row-cols-md-3 gl-mt-5"
|
||||
class="row"
|
||||
>
|
||||
<div
|
||||
class="col gl-mb-6"
|
||||
class="gl-mt-5 col-sm-12 col-mb-6 col-lg-4"
|
||||
>
|
||||
<div
|
||||
class="gl-card gl-pt-0 learn-gitlab-section-card"
|
||||
class="gl-card gl-pt-0 h-100"
|
||||
>
|
||||
<!---->
|
||||
<div
|
||||
class="gl-card-header gl-bg-white gl-border-0 gl-pb-0"
|
||||
>
|
||||
<img
|
||||
src="workspace.svg"
|
||||
/>
|
||||
|
||||
<h2
|
||||
class="gl-font-lg gl-mb-3"
|
||||
>
|
||||
Set up your workspace
|
||||
</h2>
|
||||
|
||||
<p
|
||||
class="gl-text-gray-700 gl-mb-6"
|
||||
>
|
||||
Complete these tasks first so you can enjoy GitLab's features to their fullest:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="gl-card-body"
|
||||
class="gl-card-body gl-pt-0"
|
||||
>
|
||||
<div
|
||||
class="learn-gitlab-section-card-header"
|
||||
>
|
||||
<img
|
||||
src="workspace.svg"
|
||||
/>
|
||||
|
||||
<h2
|
||||
class="gl-font-lg gl-mb-3"
|
||||
>
|
||||
Set up your workspace
|
||||
</h2>
|
||||
|
||||
<p
|
||||
class="gl-text-gray-700 gl-mb-6"
|
||||
>
|
||||
Complete these tasks first so you can enjoy GitLab's features to their fullest:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<span
|
||||
class="gl-text-green-500"
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="flex align-items-center"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="gl-icon s16"
|
||||
data-testid="completed-icon"
|
||||
role="img"
|
||||
<span
|
||||
class="gl-text-green-500"
|
||||
>
|
||||
<use
|
||||
href="#check-circle-filled"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Invite your colleagues
|
||||
|
||||
</span>
|
||||
|
||||
<!---->
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="gl-icon s16"
|
||||
data-testid="completed-icon"
|
||||
role="img"
|
||||
>
|
||||
<use
|
||||
href="#check-circle-filled"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Invite your colleagues
|
||||
|
||||
</span>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<span
|
||||
class="gl-text-green-500"
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="flex align-items-center"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="gl-icon s16"
|
||||
data-testid="completed-icon"
|
||||
role="img"
|
||||
<span
|
||||
class="gl-text-green-500"
|
||||
>
|
||||
<use
|
||||
href="#check-circle-filled"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Create or import a repository
|
||||
|
||||
</span>
|
||||
|
||||
<!---->
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="gl-icon s16"
|
||||
data-testid="completed-icon"
|
||||
role="img"
|
||||
>
|
||||
<use
|
||||
href="#check-circle-filled"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Create or import a repository
|
||||
|
||||
</span>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Set up CI/CD"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Set up CI/CD
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="flex align-items-center"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Set up CI/CD"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Set up CI/CD
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Start a free Ultimate trial"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Start a free Ultimate trial
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="flex align-items-center"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Start a free Ultimate trial"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Start a free Ultimate trial
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Add code owners"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Add code owners
|
||||
|
||||
</a>
|
||||
|
||||
<span
|
||||
<div
|
||||
class="gl-font-style-italic gl-text-gray-500"
|
||||
data-testid="trial-only"
|
||||
>
|
||||
|
||||
- Trial only
|
||||
Trial only
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex align-items-center"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Add code owners"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Add code owners
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Add merge request approval"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Add merge request approval
|
||||
|
||||
</a>
|
||||
|
||||
<span
|
||||
<div
|
||||
class="gl-font-style-italic gl-text-gray-500"
|
||||
data-testid="trial-only"
|
||||
>
|
||||
|
||||
- Trial only
|
||||
Trial only
|
||||
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex align-items-center"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Add merge request approval"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Add merge request approval
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -222,71 +256,81 @@ exports[`Learn GitLab renders correctly 1`] = `
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="col gl-mb-6"
|
||||
class="gl-mt-5 col-sm-12 col-mb-6 col-lg-4"
|
||||
>
|
||||
<div
|
||||
class="gl-card gl-pt-0 learn-gitlab-section-card"
|
||||
class="gl-card gl-pt-0 h-100"
|
||||
>
|
||||
<!---->
|
||||
<div
|
||||
class="gl-card-header gl-bg-white gl-border-0 gl-pb-0"
|
||||
>
|
||||
<img
|
||||
src="plan.svg"
|
||||
/>
|
||||
|
||||
<h2
|
||||
class="gl-font-lg gl-mb-3"
|
||||
>
|
||||
Plan and execute
|
||||
</h2>
|
||||
|
||||
<p
|
||||
class="gl-text-gray-700 gl-mb-6"
|
||||
>
|
||||
Create a workflow for your new workspace, and learn how GitLab features work together:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="gl-card-body"
|
||||
class="gl-card-body gl-pt-0"
|
||||
>
|
||||
<div
|
||||
class="learn-gitlab-section-card-header"
|
||||
>
|
||||
<img
|
||||
src="plan.svg"
|
||||
/>
|
||||
|
||||
<h2
|
||||
class="gl-font-lg gl-mb-3"
|
||||
>
|
||||
Plan and execute
|
||||
</h2>
|
||||
|
||||
<p
|
||||
class="gl-text-gray-700 gl-mb-6"
|
||||
>
|
||||
Create a workflow for your new workspace, and learn how GitLab features work together:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Create an issue"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Create an issue
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="flex align-items-center"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Create an issue"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Create an issue
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Submit a merge request"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Submit a merge request
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="flex align-items-center"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Submit a merge request"
|
||||
href="http://example.com/"
|
||||
target="_self"
|
||||
>
|
||||
|
||||
Submit a merge request
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -294,54 +338,58 @@ exports[`Learn GitLab renders correctly 1`] = `
|
|||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="col gl-mb-6"
|
||||
class="gl-mt-5 col-sm-12 col-mb-6 col-lg-4"
|
||||
>
|
||||
<div
|
||||
class="gl-card gl-pt-0 learn-gitlab-section-card"
|
||||
class="gl-card gl-pt-0 h-100"
|
||||
>
|
||||
<!---->
|
||||
<div
|
||||
class="gl-card-header gl-bg-white gl-border-0 gl-pb-0"
|
||||
>
|
||||
<img
|
||||
src="deploy.svg"
|
||||
/>
|
||||
|
||||
<h2
|
||||
class="gl-font-lg gl-mb-3"
|
||||
>
|
||||
Deploy
|
||||
</h2>
|
||||
|
||||
<p
|
||||
class="gl-text-gray-700 gl-mb-6"
|
||||
>
|
||||
Use your new GitLab workflow to deploy your application, monitor its health, and keep it secure:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="gl-card-body"
|
||||
class="gl-card-body gl-pt-0"
|
||||
>
|
||||
<div
|
||||
class="learn-gitlab-section-card-header"
|
||||
>
|
||||
<img
|
||||
src="deploy.svg"
|
||||
/>
|
||||
|
||||
<h2
|
||||
class="gl-font-lg gl-mb-3"
|
||||
>
|
||||
Deploy
|
||||
</h2>
|
||||
|
||||
<p
|
||||
class="gl-text-gray-700 gl-mb-6"
|
||||
>
|
||||
Use your new GitLab workflow to deploy your application, monitor its health, and keep it secure:
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="gl-mb-4"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Run a Security scan using CI/CD"
|
||||
href="https://docs.gitlab.com/ee/foobar/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Run a Security scan using CI/CD
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
|
||||
<div
|
||||
class="flex align-items-center"
|
||||
>
|
||||
<a
|
||||
class="gl-link"
|
||||
data-testid="uncompleted-learn-gitlab-link"
|
||||
data-track-action="click_link"
|
||||
data-track-label="Run a Security scan using CI/CD"
|
||||
href="https://docs.gitlab.com/ee/foobar/"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
Run a Security scan using CI/CD
|
||||
|
||||
</a>
|
||||
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { stubExperiments } from 'helpers/experimentation_helper';
|
||||
import { mockTracking, triggerEvent, unmockTracking } from 'helpers/tracking_helper';
|
||||
import eventHub from '~/invite_members/event_hub';
|
||||
|
@ -26,7 +26,7 @@ describe('Learn GitLab Section Link', () => {
|
|||
});
|
||||
|
||||
const createWrapper = (action = defaultAction, props = {}) => {
|
||||
wrapper = shallowMount(LearnGitlabSectionLink, {
|
||||
wrapper = mount(LearnGitlabSectionLink, {
|
||||
propsData: { action, value: { ...defaultProps, ...props } },
|
||||
});
|
||||
};
|
||||
|
@ -36,6 +36,8 @@ describe('Learn GitLab Section Link', () => {
|
|||
|
||||
const findUncompletedLink = () => wrapper.find('[data-testid="uncompleted-learn-gitlab-link"]');
|
||||
|
||||
const videoTutorialLink = () => wrapper.find('[data-testid="video-tutorial-link"]');
|
||||
|
||||
it('renders no icon when not completed', () => {
|
||||
createWrapper(undefined, { completed: false });
|
||||
|
||||
|
@ -130,4 +132,44 @@ describe('Learn GitLab Section Link', () => {
|
|||
unmockTracking();
|
||||
});
|
||||
});
|
||||
|
||||
describe('video_tutorials_continuous_onboarding experiment', () => {
|
||||
describe('when control', () => {
|
||||
beforeEach(() => {
|
||||
stubExperiments({ video_tutorials_continuous_onboarding: 'control' });
|
||||
createWrapper('codeOwnersEnabled');
|
||||
});
|
||||
|
||||
it('renders no video link', () => {
|
||||
expect(videoTutorialLink().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when candidate', () => {
|
||||
beforeEach(() => {
|
||||
stubExperiments({ video_tutorials_continuous_onboarding: 'candidate' });
|
||||
createWrapper('codeOwnersEnabled');
|
||||
});
|
||||
|
||||
it('renders video link with blank target', () => {
|
||||
const videoLinkElement = videoTutorialLink();
|
||||
|
||||
expect(videoLinkElement.exists()).toBe(true);
|
||||
expect(videoLinkElement.attributes('target')).toEqual('_blank');
|
||||
});
|
||||
|
||||
it('tracks the click', () => {
|
||||
const trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn);
|
||||
|
||||
videoTutorialLink().trigger('click');
|
||||
|
||||
expect(trackingSpy).toHaveBeenCalledWith('_category_', 'click_video_link', {
|
||||
label: 'Add code owners',
|
||||
property: 'Growth::Conversion::Experiment::LearnGitLab',
|
||||
});
|
||||
|
||||
unmockTracking();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -522,11 +522,53 @@ RSpec.describe Resolvers::IssuesResolver do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when sorting by escalation status' do
|
||||
let_it_be(:project) { create(:project, :public) }
|
||||
let_it_be(:triggered_incident) { create(:incident, :with_escalation_status, project: project) }
|
||||
let_it_be(:issue_no_status) { create(:issue, project: project) }
|
||||
let_it_be(:resolved_incident) do
|
||||
create(:incident, :with_escalation_status, project: project)
|
||||
.tap { |issue| issue.escalation_status.resolve }
|
||||
end
|
||||
|
||||
it 'sorts issues ascending' do
|
||||
issues = resolve_issues(sort: :escalation_status_asc).to_a
|
||||
expect(issues).to eq([triggered_incident, resolved_incident, issue_no_status])
|
||||
end
|
||||
|
||||
it 'sorts issues descending' do
|
||||
issues = resolve_issues(sort: :escalation_status_desc).to_a
|
||||
expect(issues).to eq([resolved_incident, triggered_incident, issue_no_status])
|
||||
end
|
||||
|
||||
it 'sorts issues created_at' do
|
||||
issues = resolve_issues(sort: :created_desc).to_a
|
||||
expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
|
||||
end
|
||||
|
||||
context 'when incident_escalations feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(incident_escalations: false)
|
||||
end
|
||||
|
||||
it 'defaults ascending status sort to created_desc' do
|
||||
issues = resolve_issues(sort: :escalation_status_asc).to_a
|
||||
expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
|
||||
end
|
||||
|
||||
it 'defaults descending status sort to created_desc' do
|
||||
issues = resolve_issues(sort: :escalation_status_desc).to_a
|
||||
expect(issues).to eq([resolved_incident, issue_no_status, triggered_incident])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when sorting with non-stable cursors' do
|
||||
%i[priority_asc priority_desc
|
||||
popularity_asc popularity_desc
|
||||
label_priority_asc label_priority_desc
|
||||
milestone_due_asc milestone_due_desc].each do |sort_by|
|
||||
milestone_due_asc milestone_due_desc
|
||||
escalation_status_asc escalation_status_desc].each do |sort_by|
|
||||
it "uses offset-pagination when sorting by #{sort_by}" do
|
||||
resolved = resolve_issues(sort: sort_by)
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['IssueSort'] do
|
|||
|
||||
it 'exposes all the existing issue sort values' do
|
||||
expect(described_class.values.keys).to include(
|
||||
*%w[DUE_DATE_ASC DUE_DATE_DESC RELATIVE_POSITION_ASC SEVERITY_ASC SEVERITY_DESC]
|
||||
*%w[DUE_DATE_ASC DUE_DATE_DESC RELATIVE_POSITION_ASC SEVERITY_ASC SEVERITY_DESC ESCALATION_STATUS_ASC ESCALATION_STATUS_DESC]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -238,6 +238,24 @@ RSpec.describe Issue do
|
|||
end
|
||||
end
|
||||
|
||||
context 'order by escalation status' do
|
||||
let_it_be(:triggered_incident) { create(:incident_management_issuable_escalation_status, :triggered).issue }
|
||||
let_it_be(:resolved_incident) { create(:incident_management_issuable_escalation_status, :resolved).issue }
|
||||
let_it_be(:issue_no_status) { create(:issue) }
|
||||
|
||||
describe '.order_escalation_status_asc' do
|
||||
subject { described_class.order_escalation_status_asc }
|
||||
|
||||
it { is_expected.to eq([triggered_incident, resolved_incident, issue_no_status]) }
|
||||
end
|
||||
|
||||
describe '.order_escalation_status_desc' do
|
||||
subject { described_class.order_escalation_status_desc }
|
||||
|
||||
it { is_expected.to eq([resolved_incident, triggered_incident, issue_no_status]) }
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Remove when NOT NULL constraint is added to the relationship
|
||||
describe '#work_item_type' do
|
||||
let(:issue) { create(:issue, :incident, project: reusable_project, work_item_type: nil) }
|
||||
|
|
|
@ -28,6 +28,17 @@ RSpec.describe Mutations::UserPreferences::Update do
|
|||
expect(current_user.user_preference.persisted?).to eq(true)
|
||||
expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s)
|
||||
end
|
||||
|
||||
context 'when incident_escalations feature flag is disabled' do
|
||||
let(:sort_value) { 'ESCALATION_STATUS_ASC' }
|
||||
|
||||
before do
|
||||
stub_feature_flags(incident_escalations: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'a mutation that returns top-level errors',
|
||||
errors: ['Feature flag `incident_escalations` must be enabled to use this sort order.']
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user has existing preference' do
|
||||
|
@ -45,5 +56,16 @@ RSpec.describe Mutations::UserPreferences::Update do
|
|||
|
||||
expect(current_user.user_preference.issues_sort).to eq(Types::IssueSortEnum.values[sort_value].value.to_s)
|
||||
end
|
||||
|
||||
context 'when incident_escalations feature flag is disabled' do
|
||||
let(:sort_value) { 'ESCALATION_STATUS_DESC' }
|
||||
|
||||
before do
|
||||
stub_feature_flags(incident_escalations: false)
|
||||
end
|
||||
|
||||
it_behaves_like 'a mutation that returns top-level errors',
|
||||
errors: ['Feature flag `incident_escalations` must be enabled to use this sort order.']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue