Commit 925b9264 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 1b19205e a5514584
...@@ -40,18 +40,23 @@ export default { ...@@ -40,18 +40,23 @@ export default {
data() { data() {
return { return {
isLoading: false, isLoading: false,
isSharedRunnerEnabled: false, isSharedRunnerEnabled: this.isEnabled,
errorMessage: null, errorMessage: null,
isCcValidationRequired: false, successfulValidation: false,
}; };
}, },
created() { computed: {
this.isSharedRunnerEnabled = this.isEnabled; showCreditCardValidation() {
this.isCcValidationRequired = this.isCreditCardValidationRequired; return (
this.isCreditCardValidationRequired &&
!this.isSharedRunnerEnabled &&
!this.successfulValidation
);
},
}, },
methods: { methods: {
creditCardValidated() { creditCardValidated() {
this.isCcValidationRequired = false; this.successfulValidation = true;
}, },
toggleSharedRunners() { toggleSharedRunners() {
this.isLoading = true; this.isLoading = true;
...@@ -62,7 +67,6 @@ export default { ...@@ -62,7 +67,6 @@ export default {
.then(() => { .then(() => {
this.isLoading = false; this.isLoading = false;
this.isSharedRunnerEnabled = !this.isSharedRunnerEnabled; this.isSharedRunnerEnabled = !this.isSharedRunnerEnabled;
this.isCcValidationRequired = this.isCreditCardValidationRequired;
}) })
.catch((error) => { .catch((error) => {
this.isLoading = false; this.isLoading = false;
...@@ -81,7 +85,7 @@ export default { ...@@ -81,7 +85,7 @@ export default {
</gl-alert> </gl-alert>
<cc-validation-required-alert <cc-validation-required-alert
v-if="isCcValidationRequired && !isSharedRunnerEnabled" v-if="showCreditCardValidation"
class="gl-pb-5" class="gl-pb-5"
:custom-message="$options.i18n.REQUIRES_VALIDATION_TEXT" :custom-message="$options.i18n.REQUIRES_VALIDATION_TEXT"
@verifiedCreditCard="creditCardValidated" @verifiedCreditCard="creditCardValidated"
......
- navbar_links = links.sort_by(&:title) - navbar_links = links.sort_by(&:title)
- all_paths = navbar_links.map(&:path) - all_paths = navbar_links.map(&:path)
- analytics_link = navbar_links.find { |link| link.title == _('Value Stream') } || navbar_links.first - analytics_link = navbar_links.find { |link| link.title == _('Value stream') } || navbar_links.first
- if navbar_links.any? - if navbar_links.any?
= nav_link(path: all_paths) do = nav_link(path: all_paths) do
......
...@@ -76,13 +76,13 @@ export default { ...@@ -76,13 +76,13 @@ export default {
this.rules = this.form.rules.map((rule) => { this.rules = this.form.rules.map((rule) => {
const { const {
status, status,
elapsedTimeSeconds, elapsedTimeMinutes,
oncallSchedule: { iid: oncallScheduleIid }, oncallSchedule: { iid: oncallScheduleIid },
} = rule; } = rule;
return { return {
status, status,
elapsedTimeSeconds, elapsedTimeMinutes,
action: DEFAULT_ACTION, action: DEFAULT_ACTION,
oncallScheduleIid, oncallScheduleIid,
key: uniqueId(), key: uniqueId(),
......
...@@ -9,7 +9,7 @@ import { ...@@ -9,7 +9,7 @@ import {
import createEscalationPolicyMutation from '../graphql/mutations/create_escalation_policy.mutation.graphql'; import createEscalationPolicyMutation from '../graphql/mutations/create_escalation_policy.mutation.graphql';
import updateEscalationPolicyMutation from '../graphql/mutations/update_escalation_policy.mutation.graphql'; import updateEscalationPolicyMutation from '../graphql/mutations/update_escalation_policy.mutation.graphql';
import getEscalationPoliciesQuery from '../graphql/queries/get_escalation_policies.query.graphql'; import getEscalationPoliciesQuery from '../graphql/queries/get_escalation_policies.query.graphql';
import { isNameFieldValid, getRulesValidationState } from '../utils'; import { isNameFieldValid, getRulesValidationState, serializeRule } from '../utils';
import AddEditEscalationPolicyForm from './add_edit_escalation_policy_form.vue'; import AddEditEscalationPolicyForm from './add_edit_escalation_policy_form.vue';
export const i18n = { export const i18n = {
...@@ -91,7 +91,7 @@ export default { ...@@ -91,7 +91,7 @@ export default {
}, },
requestParams() { requestParams() {
const id = this.isEditMode ? { id: this.escalationPolicy.id } : {}; const id = this.isEditMode ? { id: this.escalationPolicy.id } : {};
return { ...this.form, ...id, rules: this.getRules(this.form.rules) }; return { ...this.form, ...id, rules: this.getRules(this.form.rules).map(serializeRule) };
}, },
}, },
methods: { methods: {
...@@ -186,9 +186,9 @@ export default { ...@@ -186,9 +186,9 @@ export default {
}, },
getRules(rules) { getRules(rules) {
return rules.map( return rules.map(
({ status, elapsedTimeSeconds, oncallScheduleIid, oncallSchedule: { iid } = {} }) => ({ ({ status, elapsedTimeMinutes, oncallScheduleIid, oncallSchedule: { iid } = {} }) => ({
status, status,
elapsedTimeSeconds, elapsedTimeMinutes,
oncallScheduleIid: oncallScheduleIid || iid, oncallScheduleIid: oncallScheduleIid || iid,
}), }),
); );
......
...@@ -4,6 +4,7 @@ import * as Sentry from '@sentry/browser'; ...@@ -4,6 +4,7 @@ import * as Sentry from '@sentry/browser';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { addEscalationPolicyModalId } from '../constants'; import { addEscalationPolicyModalId } from '../constants';
import getEscalationPoliciesQuery from '../graphql/queries/get_escalation_policies.query.graphql'; import getEscalationPoliciesQuery from '../graphql/queries/get_escalation_policies.query.graphql';
import { parsePolicy } from '../utils';
import AddEscalationPolicyModal from './add_edit_escalation_policy_modal.vue'; import AddEscalationPolicyModal from './add_edit_escalation_policy_modal.vue';
import EscalationPolicy from './escalation_policy.vue'; import EscalationPolicy from './escalation_policy.vue';
...@@ -47,7 +48,7 @@ export default { ...@@ -47,7 +48,7 @@ export default {
}; };
}, },
update({ project }) { update({ project }) {
return project?.incidentManagementEscalationPolicies?.nodes ?? []; return project?.incidentManagementEscalationPolicies?.nodes.map(parsePolicy) ?? [];
}, },
error(error) { error(error) {
Sentry.captureException(error); Sentry.captureException(error);
......
...@@ -29,9 +29,9 @@ export const i18n = { ...@@ -29,9 +29,9 @@ export const i18n = {
minutes: s__('EscalationPolicies|mins'), minutes: s__('EscalationPolicies|mins'),
}; };
const isRuleValid = ({ status, elapsedTimeSeconds, oncallSchedule: { name } }) => const isRuleValid = ({ status, elapsedTimeMinutes, oncallSchedule: { name } }) =>
Object.keys(ALERT_STATUSES).includes(status) && Object.keys(ALERT_STATUSES).includes(status) &&
typeof elapsedTimeSeconds === 'number' && typeof elapsedTimeMinutes === 'number' &&
typeof name === 'string'; typeof name === 'string';
export default { export default {
...@@ -145,7 +145,7 @@ export default { ...@@ -145,7 +145,7 @@ export default {
</template> </template>
<template #minutes> <template #minutes>
<span class="gl-font-weight-bold"> <span class="gl-font-weight-bold">
{{ rule.elapsedTimeSeconds }} {{ $options.i18n.minutes }} {{ rule.elapsedTimeMinutes }} {{ $options.i18n.minutes }}
</span> </span>
</template> </template>
<template #then> <template #then>
......
...@@ -76,10 +76,10 @@ export default { ...@@ -76,10 +76,10 @@ export default {
}, },
}, },
data() { data() {
const { status, elapsedTimeSeconds, action, oncallScheduleIid } = this.rule; const { status, elapsedTimeMinutes, action, oncallScheduleIid } = this.rule;
return { return {
status, status,
elapsedTimeSeconds, elapsedTimeMinutes,
action, action,
oncallScheduleIid, oncallScheduleIid,
}; };
...@@ -119,7 +119,7 @@ export default { ...@@ -119,7 +119,7 @@ export default {
oncallScheduleIid: parseInt(this.oncallScheduleIid, 10), oncallScheduleIid: parseInt(this.oncallScheduleIid, 10),
action: this.action, action: this.action,
status: this.status, status: this.status,
elapsedTimeSeconds: parseInt(this.elapsedTimeSeconds, 10), elapsedTimeMinutes: this.elapsedTimeMinutes,
}, },
}); });
}, },
...@@ -169,9 +169,9 @@ export default { ...@@ -169,9 +169,9 @@ export default {
</template> </template>
<template #minutes> <template #minutes>
<gl-form-input <gl-form-input
v-model="elapsedTimeSeconds" v-model="elapsedTimeMinutes"
class="gl-mx-3 gl-inset-border-1-gray-200! gl-w-12" class="gl-mx-3 gl-inset-border-1-gray-200! gl-w-12"
type="number" number
min="0" min="0"
@input="emitUpdate" @input="emitUpdate"
/> />
......
...@@ -13,7 +13,7 @@ export const ACTIONS = { ...@@ -13,7 +13,7 @@ export const ACTIONS = {
export const DEFAULT_ESCALATION_RULE = { export const DEFAULT_ESCALATION_RULE = {
status: 'ACKNOWLEDGED', status: 'ACKNOWLEDGED',
elapsedTimeSeconds: 0, elapsedTimeMinutes: 0,
action: 'EMAIL_ONCALL_SCHEDULE_USER', action: 'EMAIL_ONCALL_SCHEDULE_USER',
oncallScheduleIid: null, oncallScheduleIid: null,
}; };
......
...@@ -17,8 +17,33 @@ export const isNameFieldValid = (name) => { ...@@ -17,8 +17,33 @@ export const isNameFieldValid = (name) => {
export const getRulesValidationState = (rules) => { export const getRulesValidationState = (rules) => {
return rules.map((rule) => { return rules.map((rule) => {
return { return {
isTimeValid: parseInt(rule.elapsedTimeSeconds, 10) >= 0, isTimeValid: parseInt(rule.elapsedTimeMinutes, 10) >= 0,
isScheduleValid: Boolean(rule.oncallScheduleIid), isScheduleValid: Boolean(rule.oncallScheduleIid),
}; };
}); });
}; };
/**
* Serializes a rule by converting elapsed minutes to seconds
* @param {Object} rule
*
* @returns {Object} rule
*/
export const serializeRule = ({ elapsedTimeMinutes, ...ruleParams }) => ({
...ruleParams,
elapsedTimeSeconds: elapsedTimeMinutes * 60,
});
/**
* Parses a policy by converting elapsed seconds to minutes
* @param {Object} policy
*
* @returns {Object} policy
*/
export const parsePolicy = (policy) => ({
...policy,
rules: policy.rules.map(({ elapsedTimeSeconds, ...ruleParams }) => ({
...ruleParams,
elapsedTimeMinutes: elapsedTimeSeconds / 60,
})),
});
...@@ -28,7 +28,7 @@ module EE ...@@ -28,7 +28,7 @@ module EE
return unless group_sidebar_link?(:merge_request_analytics) # rubocop: disable Lint/UnreachableCode return unless group_sidebar_link?(:merge_request_analytics) # rubocop: disable Lint/UnreachableCode
navbar_sub_item( navbar_sub_item(
title: _('Merge Request'), title: _('Merge request'),
path: 'groups/analytics/merge_request_analytics#show', path: 'groups/analytics/merge_request_analytics#show',
link: group_analytics_merge_request_analytics_path(group) link: group_analytics_merge_request_analytics_path(group)
) )
...@@ -38,7 +38,7 @@ module EE ...@@ -38,7 +38,7 @@ module EE
return unless group_sidebar_link?(:cycle_analytics) return unless group_sidebar_link?(:cycle_analytics)
navbar_sub_item( navbar_sub_item(
title: _('Value Stream'), title: _('Value stream'),
path: 'groups/analytics/cycle_analytics#show', path: 'groups/analytics/cycle_analytics#show',
link: group_analytics_cycle_analytics_path(group) link: group_analytics_cycle_analytics_path(group)
) )
...@@ -48,7 +48,7 @@ module EE ...@@ -48,7 +48,7 @@ module EE
return unless group_sidebar_link?(:group_devops_adoption) return unless group_sidebar_link?(:group_devops_adoption)
navbar_sub_item( navbar_sub_item(
title: _('DevOps Adoption'), title: _('DevOps adoption'),
path: 'groups/analytics/devops_adoption#show', path: 'groups/analytics/devops_adoption#show',
link: group_analytics_devops_adoption_path(group) link: group_analytics_devops_adoption_path(group)
) )
......
...@@ -44,7 +44,7 @@ module EE ...@@ -44,7 +44,7 @@ module EE
end end
::Sidebars::MenuItem.new( ::Sidebars::MenuItem.new(
title: _('Code Review'), title: _('Code review'),
link: project_analytics_code_reviews_path(context.project), link: project_analytics_code_reviews_path(context.project),
active_routes: { path: 'projects/analytics/code_reviews#index' }, active_routes: { path: 'projects/analytics/code_reviews#index' },
item_id: :code_review item_id: :code_review
...@@ -76,7 +76,7 @@ module EE ...@@ -76,7 +76,7 @@ module EE
end end
::Sidebars::MenuItem.new( ::Sidebars::MenuItem.new(
title: _('Merge Request'), title: _('Merge request'),
link: project_analytics_merge_request_analytics_path(context.project), link: project_analytics_merge_request_analytics_path(context.project),
active_routes: { path: 'projects/analytics/merge_request_analytics#show' }, active_routes: { path: 'projects/analytics/merge_request_analytics#show' },
item_id: :merge_requests item_id: :merge_requests
......
...@@ -52,7 +52,7 @@ RSpec.describe Projects::Analytics::MergeRequestAnalyticsController do ...@@ -52,7 +52,7 @@ RSpec.describe Projects::Analytics::MergeRequestAnalyticsController do
end end
it 'renders the side navigation with the correct submenu set as active' do it 'renders the side navigation with the correct submenu set as active' do
expect(response.body).to have_active_sub_navigation('Merge Request') expect(response.body).to have_active_sub_navigation('Merge request')
end end
end end
end end
......
...@@ -28,7 +28,7 @@ RSpec.describe 'Group navbar' do ...@@ -28,7 +28,7 @@ RSpec.describe 'Group navbar' do
insert_after_sub_nav_item( insert_after_sub_nav_item(
_('Contribution'), _('Contribution'),
within: _('Analytics'), within: _('Analytics'),
new_sub_nav_item_name: _('DevOps Adoption') new_sub_nav_item_name: _('DevOps adoption')
) )
visit group_path(group) visit group_path(group)
...@@ -60,7 +60,7 @@ RSpec.describe 'Group navbar' do ...@@ -60,7 +60,7 @@ RSpec.describe 'Group navbar' do
insert_after_sub_nav_item( insert_after_sub_nav_item(
_('Contribution'), _('Contribution'),
within: _('Analytics'), within: _('Analytics'),
new_sub_nav_item_name: _('Value Stream') new_sub_nav_item_name: _('Value stream')
) )
visit group_path(group) visit group_path(group)
......
...@@ -30,7 +30,7 @@ RSpec.describe 'Project active tab' do ...@@ -30,7 +30,7 @@ RSpec.describe 'Project active tab' do
end end
it_behaves_like 'page has active tab', _('Analytics') it_behaves_like 'page has active tab', _('Analytics')
it_behaves_like 'page has active sub tab', _('Code Review') it_behaves_like 'page has active sub tab', _('Code review')
end end
context 'on project CI/CD' do context 'on project CI/CD' do
......
...@@ -23,7 +23,7 @@ RSpec.describe 'Project navbar' do ...@@ -23,7 +23,7 @@ RSpec.describe 'Project navbar' do
stub_licensed_features(issues_analytics: true) stub_licensed_features(issues_analytics: true)
insert_after_sub_nav_item( insert_after_sub_nav_item(
_('Code Review'), _('Code review'),
within: _('Analytics'), within: _('Analytics'),
new_sub_nav_item_name: _('Issue') new_sub_nav_item_name: _('Issue')
) )
......
...@@ -38,7 +38,7 @@ exports[`EscalationPolicy renders policy with rules 1`] = ` ...@@ -38,7 +38,7 @@ exports[`EscalationPolicy renders policy with rules 1`] = `
class="gl-font-weight-bold" class="gl-font-weight-bold"
> >
10 mins 1 mins
</span> </span>
...@@ -81,7 +81,7 @@ exports[`EscalationPolicy renders policy with rules 1`] = ` ...@@ -81,7 +81,7 @@ exports[`EscalationPolicy renders policy with rules 1`] = `
class="gl-font-weight-bold" class="gl-font-weight-bold"
> >
20 mins 2 mins
</span> </span>
......
...@@ -82,7 +82,7 @@ describe('AddEscalationPolicyForm', () => { ...@@ -82,7 +82,7 @@ describe('AddEscalationPolicyForm', () => {
it('on rule update emitted should update rules array and emit updates up', () => { it('on rule update emitted should update rules array and emit updates up', () => {
const updatedRule = { const updatedRule = {
status: 'TRIGGERED', status: 'TRIGGERED',
elapsedTimeSeconds: 30, elapsedTimeMinutes: 3,
oncallScheduleIid: 2, oncallScheduleIid: 2,
}; };
findRules().at(0).vm.$emit('update-escalation-rule', { index: 0, rule: updatedRule }); findRules().at(0).vm.$emit('update-escalation-rule', { index: 0, rule: updatedRule });
......
...@@ -23,7 +23,8 @@ describe('AddEditsEscalationPolicyModal', () => { ...@@ -23,7 +23,8 @@ describe('AddEditsEscalationPolicyModal', () => {
const mockEscalationPolicy = cloneDeep(mockPolicies[0]); const mockEscalationPolicy = cloneDeep(mockPolicies[0]);
const updatedName = 'Policy name'; const updatedName = 'Policy name';
const updatedDescription = 'Policy description'; const updatedDescription = 'Policy description';
const updatedRules = [{ status: 'RESOLVED', elapsedTimeSeconds: 10, oncallScheduleIid: 1 }]; const updatedRules = [{ status: 'RESOLVED', elapsedTimeMinutes: 1, oncallScheduleIid: 1 }];
const serializedRules = [{ status: 'RESOLVED', elapsedTimeSeconds: 60, oncallScheduleIid: 1 }];
const createComponent = ({ escalationPolicy, isEditMode = false, modalId, data } = {}) => { const createComponent = ({ escalationPolicy, isEditMode = false, modalId, data } = {}) => {
wrapper = shallowMount(AddEscalationPolicyModal, { wrapper = shallowMount(AddEscalationPolicyModal, {
...@@ -99,7 +100,7 @@ describe('AddEditsEscalationPolicyModal', () => { ...@@ -99,7 +100,7 @@ describe('AddEditsEscalationPolicyModal', () => {
projectPath, projectPath,
name: updatedName, name: updatedName,
description: updatedDescription, description: updatedDescription,
rules: updatedRules, rules: serializedRules,
}, },
}, },
update: expect.any(Function), update: expect.any(Function),
...@@ -168,7 +169,7 @@ describe('AddEditsEscalationPolicyModal', () => { ...@@ -168,7 +169,7 @@ describe('AddEditsEscalationPolicyModal', () => {
input: { input: {
name: updatedName, name: updatedName,
description: updatedDescription, description: updatedDescription,
rules: updatedRules, rules: serializedRules,
id: mockEscalationPolicy.id, id: mockEscalationPolicy.id,
}, },
}, },
...@@ -264,7 +265,7 @@ describe('AddEditsEscalationPolicyModal', () => { ...@@ -264,7 +265,7 @@ describe('AddEditsEscalationPolicyModal', () => {
}); });
form.vm.$emit('update-escalation-policy-form', { form.vm.$emit('update-escalation-policy-form', {
field: 'rules', field: 'rules',
value: [{ status: 'RESOLVED', elapsedTimeSeconds: 10, oncallScheduleIid: 1 }], value: [{ status: 'RESOLVED', elapsedTimeMinutes: 1, oncallScheduleIid: 1 }],
}); });
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(findModal().props('actionPrimary').attributes).toContainEqual({ disabled: false }); expect(findModal().props('actionPrimary').attributes).toContainEqual({ disabled: false });
......
...@@ -9,11 +9,12 @@ import { ...@@ -9,11 +9,12 @@ import {
deleteEscalationPolicyModalId, deleteEscalationPolicyModalId,
editEscalationPolicyModalId, editEscalationPolicyModalId,
} from 'ee/escalation_policies/constants'; } from 'ee/escalation_policies/constants';
import { parsePolicy } from 'ee/escalation_policies/utils';
import mockPolicies from './mocks/mockPolicies.json'; import mockPolicies from './mocks/mockPolicies.json';
describe('EscalationPolicy', () => { describe('EscalationPolicy', () => {
let wrapper; let wrapper;
const escalationPolicy = cloneDeep(mockPolicies[0]); const escalationPolicy = parsePolicy(cloneDeep(mockPolicies[0]));
const createComponent = () => { const createComponent = () => {
wrapper = shallowMount(EscalationPolicy, { wrapper = shallowMount(EscalationPolicy, {
......
import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui'; import { GlEmptyState, GlLoadingIcon } from '@gitlab/ui';
import EscalationPoliciesWrapper from 'ee/escalation_policies/components/escalation_policies_wrapper.vue'; import EscalationPoliciesWrapper from 'ee/escalation_policies/components/escalation_policies_wrapper.vue';
import EscalationPolicy from 'ee/escalation_policies/components/escalation_policy.vue'; import EscalationPolicy from 'ee/escalation_policies/components/escalation_policy.vue';
import { parsePolicy } from 'ee/escalation_policies/utils';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import mockEscalationPolicies from './mocks/mockPolicies.json'; import mockEscalationPolicies from './mocks/mockPolicies.json';
...@@ -55,7 +56,7 @@ describe('Escalation Policies Wrapper', () => { ...@@ -55,7 +56,7 @@ describe('Escalation Policies Wrapper', () => {
beforeEach(() => { beforeEach(() => {
mountComponent({ mountComponent({
loading, loading,
escalationPolicies, escalationPolicies: escalationPolicies.map(parsePolicy),
}); });
}); });
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
{ {
"id": "gid://gitlab/IncidentManagement::EscalationRule/22", "id": "gid://gitlab/IncidentManagement::EscalationRule/22",
"status": "ACKNOWLEDGED", "status": "ACKNOWLEDGED",
"elapsedTimeSeconds": 10, "elapsedTimeSeconds": 60,
"oncallSchedule": { "oncallSchedule": {
"iid": "3", "iid": "3",
"name": "Schedule to fill in" "name": "Schedule to fill in"
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
{ {
"id": "gid://gitlab/IncidentManagement::EscalationRule/23", "id": "gid://gitlab/IncidentManagement::EscalationRule/23",
"status": "RESOLVED", "status": "RESOLVED",
"elapsedTimeSeconds": 20, "elapsedTimeSeconds": 120,
"oncallSchedule": { "oncallSchedule": {
"iid": "4", "iid": "4",
"name": "Monitor schedule" "name": "Monitor schedule"
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
{ {
"id": "gid://gitlab/IncidentManagement::EscalationRule/48", "id": "gid://gitlab/IncidentManagement::EscalationRule/48",
"status": "ACKNOWLEDGED", "status": "ACKNOWLEDGED",
"elapsedTimeSeconds": 30, "elapsedTimeSeconds": 180,
"oncallSchedule": { "oncallSchedule": {
"iid": "3", "iid": "3",
"name": "Schedule to fill in" "name": "Schedule to fill in"
......
import { GlToggle } from '@gitlab/ui'; import { GlToggle } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import MockAxiosAdapter from 'axios-mock-adapter';
import CcValidationRequiredAlert from 'ee_component/billings/components/cc_validation_required_alert.vue'; import CcValidationRequiredAlert from 'ee_component/billings/components/cc_validation_required_alert.vue';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import SharedRunnersToggleComponent from '~/projects/settings/components/shared_runners_toggle.vue'; import SharedRunnersToggleComponent from '~/projects/settings/components/shared_runners_toggle.vue';
const TEST_UPDATE_PATH = '/test/update_shared_runners'; const TEST_UPDATE_PATH = '/test/update_shared_runners';
describe('projects/settings/components/shared_runners', () => { describe('projects/settings/components/shared_runners', () => {
let wrapper; let wrapper;
let mockAxios;
const createComponent = (props = {}) => { const createComponent = (props = {}) => {
wrapper = shallowMount(SharedRunnersToggleComponent, { wrapper = shallowMount(SharedRunnersToggleComponent, {
...@@ -28,6 +31,11 @@ describe('projects/settings/components/shared_runners', () => { ...@@ -28,6 +31,11 @@ describe('projects/settings/components/shared_runners', () => {
const getToggleValue = () => findSharedRunnersToggle().props('value'); const getToggleValue = () => findSharedRunnersToggle().props('value');
const isToggleDisabled = () => findSharedRunnersToggle().props('disabled'); const isToggleDisabled = () => findSharedRunnersToggle().props('disabled');
beforeEach(() => {
mockAxios = new MockAxiosAdapter(axios);
mockAxios.onPost(TEST_UPDATE_PATH).reply(200);
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
}); });
...@@ -57,14 +65,31 @@ describe('projects/settings/components/shared_runners', () => { ...@@ -57,14 +65,31 @@ describe('projects/settings/components/shared_runners', () => {
}); });
describe('when credit card is validated', () => { describe('when credit card is validated', () => {
it('should show the toggle button', async () => { beforeEach(() => {
findCcValidationRequiredAlert().vm.$emit('verifiedCreditCard'); findCcValidationRequiredAlert().vm.$emit('verifiedCreditCard');
await waitForPromises(); });
it('should show the toggle button', () => {
expect(findSharedRunnersToggle().exists()).toBe(true); expect(findSharedRunnersToggle().exists()).toBe(true);
expect(getToggleValue()).toBe(false); expect(getToggleValue()).toBe(false);
expect(isToggleDisabled()).toBe(false); expect(isToggleDisabled()).toBe(false);
}); });
it('should not show credit card alert after toggling on and off', async () => {
findSharedRunnersToggle().vm.$emit('change', true);
await waitForPromises();
expect(mockAxios.history.post[0].data).toBeUndefined();
expect(mockAxios.history.post).toHaveLength(1);
expect(findCcValidationRequiredAlert().exists()).toBe(false);
findSharedRunnersToggle().vm.$emit('change', false);
await waitForPromises();
expect(mockAxios.history.post[1].data).toBeUndefined();
expect(mockAxios.history.post).toHaveLength(2);
expect(findCcValidationRequiredAlert().exists()).toBe(false);
});
}); });
}); });
}); });
...@@ -93,7 +93,7 @@ RSpec.describe 'layouts/nav/sidebar/_group' do ...@@ -93,7 +93,7 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
it 'is visible' do it 'is visible' do
render render
expect(rendered).to have_text 'DevOps Adoption' expect(rendered).to have_text 'DevOps adoption'
end end
end end
...@@ -105,7 +105,7 @@ RSpec.describe 'layouts/nav/sidebar/_group' do ...@@ -105,7 +105,7 @@ RSpec.describe 'layouts/nav/sidebar/_group' do
it 'is not visible' do it 'is not visible' do
render render
expect(rendered).not_to have_text 'DevOps Adoption' expect(rendered).not_to have_text 'DevOps adoption'
end end
end end
end end
......
...@@ -282,7 +282,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -282,7 +282,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
it 'has a link to the Code Review analytics page' do it 'has a link to the Code Review analytics page' do
render render
expect(rendered).to have_link('Code Review', href: project_analytics_code_reviews_path(project)) expect(rendered).to have_link('Code review', href: project_analytics_code_reviews_path(project))
end end
context 'when user does not have access' do context 'when user does not have access' do
...@@ -291,7 +291,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -291,7 +291,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
it 'does not have a link to the Code Review analytics page' do it 'does not have a link to the Code Review analytics page' do
render render
expect(rendered).not_to have_link('Code Review', href: project_analytics_code_reviews_path(project)) expect(rendered).not_to have_link('Code review', href: project_analytics_code_reviews_path(project))
end end
end end
end end
...@@ -340,7 +340,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -340,7 +340,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end end
end end
describe 'Merge Request' do describe 'Merge request' do
before do before do
stub_licensed_features(project_merge_request_analytics: true) stub_licensed_features(project_merge_request_analytics: true)
end end
...@@ -348,7 +348,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -348,7 +348,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
it 'has a link to the merge request analytics page' do it 'has a link to the merge request analytics page' do
render render
expect(rendered).to have_link('Merge Request', href: project_analytics_merge_request_analytics_path(project)) expect(rendered).to have_link('Merge request', href: project_analytics_merge_request_analytics_path(project))
end end
context 'when user does not have access' do context 'when user does not have access' do
...@@ -357,7 +357,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -357,7 +357,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
it 'does not have a link to the merge request analytics page' do it 'does not have a link to the merge request analytics page' do
render render
expect(rendered).not_to have_link('Merge Request', href: project_analytics_merge_request_analytics_path(project)) expect(rendered).not_to have_link('Merge request', href: project_analytics_merge_request_analytics_path(project))
end end
end end
end end
......
...@@ -79,7 +79,7 @@ module Sidebars ...@@ -79,7 +79,7 @@ module Sidebars
end end
::Sidebars::MenuItem.new( ::Sidebars::MenuItem.new(
title: _('Value Stream'), title: _('Value stream'),
link: project_cycle_analytics_path(context.project), link: project_cycle_analytics_path(context.project),
container_html_options: { class: 'shortcuts-project-cycle-analytics' }, container_html_options: { class: 'shortcuts-project-cycle-analytics' },
active_routes: { path: 'cycle_analytics#show' }, active_routes: { path: 'cycle_analytics#show' },
......
...@@ -7741,6 +7741,9 @@ msgstr "" ...@@ -7741,6 +7741,9 @@ msgstr ""
msgid "Code owners" msgid "Code owners"
msgstr "" msgstr ""
msgid "Code review"
msgstr ""
msgid "Code snippet copied. Insert it in the correct location in the YAML file." msgid "Code snippet copied. Insert it in the correct location in the YAML file."
msgstr "" msgstr ""
...@@ -11069,6 +11072,9 @@ msgstr "" ...@@ -11069,6 +11072,9 @@ msgstr ""
msgid "DevOps Report" msgid "DevOps Report"
msgstr "" msgstr ""
msgid "DevOps adoption"
msgstr ""
msgid "DevopsAdoption|Add Group" msgid "DevopsAdoption|Add Group"
msgstr "" msgstr ""
...@@ -35673,9 +35679,6 @@ msgstr "" ...@@ -35673,9 +35679,6 @@ msgstr ""
msgid "Value" msgid "Value"
msgstr "" msgstr ""
msgid "Value Stream"
msgstr ""
msgid "Value Stream Analytics" msgid "Value Stream Analytics"
msgstr "" msgstr ""
...@@ -35685,6 +35688,9 @@ msgstr "" ...@@ -35685,6 +35688,9 @@ msgstr ""
msgid "Value Stream Analytics gives an overview of how much time it takes to go from idea to production in your project." msgid "Value Stream Analytics gives an overview of how much time it takes to go from idea to production in your project."
msgstr "" msgstr ""
msgid "Value stream"
msgstr ""
msgid "ValueStreamAnalyticsStage|We don't have enough data to show this stage." msgid "ValueStreamAnalyticsStage|We don't have enough data to show this stage."
msgstr "" msgstr ""
......
...@@ -138,9 +138,9 @@ RSpec.describe 'Project active tab' do ...@@ -138,9 +138,9 @@ RSpec.describe 'Project active tab' do
visit project_cycle_analytics_path(project) visit project_cycle_analytics_path(project)
end end
context 'on project Analytics/Value Stream Analytics' do context 'on project Analytics/Value stream Analytics' do
it_behaves_like 'page has active tab', _('Analytics') it_behaves_like 'page has active tab', _('Analytics')
it_behaves_like 'page has active sub tab', _('Value Stream') it_behaves_like 'page has active sub tab', _('Value stream')
end end
context 'on project Analytics/"CI/CD"' do context 'on project Analytics/"CI/CD"' do
......
...@@ -95,10 +95,10 @@ RSpec.shared_context 'project navbar structure' do ...@@ -95,10 +95,10 @@ RSpec.shared_context 'project navbar structure' do
nav_item: _('Analytics'), nav_item: _('Analytics'),
nav_sub_items: [ nav_sub_items: [
_('CI/CD'), _('CI/CD'),
(_('Code Review') if Gitlab.ee?), (_('Code review') if Gitlab.ee?),
(_('Merge Request') if Gitlab.ee?), (_('Merge request') if Gitlab.ee?),
_('Repository'), _('Repository'),
_('Value Stream') _('Value stream')
] ]
}, },
{ {
......
...@@ -695,11 +695,11 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -695,11 +695,11 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
end end
end end
describe 'Value Stream' do describe 'Value stream' do
it 'has a link to the value stream page' do it 'has a link to the value stream page' do
render render
expect(rendered).to have_link('Value Stream', href: project_cycle_analytics_path(project)) expect(rendered).to have_link('Value stream', href: project_cycle_analytics_path(project))
end end
context 'when user does not have access' do context 'when user does not have access' do
...@@ -708,7 +708,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do ...@@ -708,7 +708,7 @@ RSpec.describe 'layouts/nav/sidebar/_project' do
it 'does not have a link to the value stream page' do it 'does not have a link to the value stream page' do
render render
expect(rendered).not_to have_link('Value Stream', href: project_cycle_analytics_path(project)) expect(rendered).not_to have_link('Value stream', href: project_cycle_analytics_path(project))
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment