Commit 96df132a authored by Jacques Erasmus's avatar Jacques Erasmus

Merge branch '339949-action-based-dismissable-credit-card-validation-alert' into 'master'

Trigger shared runner credit card validation alert on click, make it always dismissable

See merge request gitlab-org/gitlab!79565
parents a3bdb419 465394d1
import { __ } from '~/locale';
import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
export const VARIABLE_TYPE = 'env_var'; export const VARIABLE_TYPE = 'env_var';
...@@ -7,5 +8,7 @@ export const CONFIG_VARIABLES_TIMEOUT = 5000; ...@@ -7,5 +8,7 @@ export const CONFIG_VARIABLES_TIMEOUT = 5000;
export const BRANCH_REF_TYPE = 'branch'; export const BRANCH_REF_TYPE = 'branch';
export const TAG_REF_TYPE = 'tag'; export const TAG_REF_TYPE = 'tag';
export const CC_VALIDATION_REQUIRED_ERROR = // must match pipeline/chain/validate/after_config.rb
'Credit card required to be on file in order to create a pipeline'; export const CC_VALIDATION_REQUIRED_ERROR = __(
'Credit card required to be on file in order to create a pipeline',
);
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { GlAlert, GlToggle, GlTooltip } from '@gitlab/ui'; import { GlAlert, GlToggle, GlTooltip } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import { CC_VALIDATION_REQUIRED_ERROR } from '../constants';
const DEFAULT_ERROR_MESSAGE = __('An error occurred while updating the configuration.'); const DEFAULT_ERROR_MESSAGE = __('An error occurred while updating the configuration.');
const REQUIRES_VALIDATION_TEXT = s__( const REQUIRES_VALIDATION_TEXT = s__(
...@@ -47,11 +48,13 @@ export default { ...@@ -47,11 +48,13 @@ export default {
}; };
}, },
computed: { computed: {
showCreditCardValidation() { ccRequiredError() {
return this.errorMessage === CC_VALIDATION_REQUIRED_ERROR && !this.ccAlertDismissed;
},
genericError() {
return ( return (
this.isCreditCardValidationRequired && this.errorMessage &&
!this.isSharedRunnerEnabled && this.errorMessage !== CC_VALIDATION_REQUIRED_ERROR &&
!this.successfulValidation &&
!this.ccAlertDismissed !this.ccAlertDismissed
); );
}, },
...@@ -62,6 +65,7 @@ export default { ...@@ -62,6 +65,7 @@ export default {
}, },
toggleSharedRunners() { toggleSharedRunners() {
this.isLoading = true; this.isLoading = true;
this.ccAlertDismissed = false;
this.errorMessage = null; this.errorMessage = null;
axios axios
...@@ -82,20 +86,19 @@ export default { ...@@ -82,20 +86,19 @@ export default {
<template> <template>
<div> <div>
<section class="gl-mt-5"> <section class="gl-mt-5">
<gl-alert v-if="errorMessage" class="gl-mb-3" variant="danger" :dismissible="false">
{{ errorMessage }}
</gl-alert>
<cc-validation-required-alert <cc-validation-required-alert
v-if="showCreditCardValidation" v-if="ccRequiredError"
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"
@dismiss="ccAlertDismissed = true" @dismiss="ccAlertDismissed = true"
/> />
<gl-alert v-if="genericError" class="gl-mb-3" variant="danger" :dismissible="false">
{{ errorMessage }}
</gl-alert>
<gl-toggle <gl-toggle
v-else
ref="sharedRunnersToggle" ref="sharedRunnersToggle"
:disabled="isDisabledAndUnoverridable" :disabled="isDisabledAndUnoverridable"
:is-loading="isLoading" :is-loading="isLoading"
......
import { __ } from '~/locale';
export const LEVEL_TYPES = { export const LEVEL_TYPES = {
ROLE: 'role', ROLE: 'role',
USER: 'user', USER: 'user',
...@@ -18,3 +20,8 @@ export const ACCESS_LEVELS = { ...@@ -18,3 +20,8 @@ export const ACCESS_LEVELS = {
}; };
export const ACCESS_LEVEL_NONE = 0; export const ACCESS_LEVEL_NONE = 0;
// must match shared_runners_setting in update_service.rb
export const CC_VALIDATION_REQUIRED_ERROR = __(
'Shared runners enabled cannot be enabled until a valid credit card is on file',
);
import { GlToggle } from '@gitlab/ui'; import { GlToggle, GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import MockAxiosAdapter from 'axios-mock-adapter'; import MockAxiosAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
...@@ -7,6 +7,7 @@ import { TEST_HOST } from 'helpers/test_constants'; ...@@ -7,6 +7,7 @@ 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 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';
import { CC_VALIDATION_REQUIRED_ERROR } from '~/projects/settings/constants';
const TEST_UPDATE_PATH = '/test/update_shared_runners'; const TEST_UPDATE_PATH = '/test/update_shared_runners';
...@@ -29,6 +30,7 @@ describe('projects/settings/components/shared_runners', () => { ...@@ -29,6 +30,7 @@ describe('projects/settings/components/shared_runners', () => {
const findSharedRunnersToggle = () => wrapper.findComponent(GlToggle); const findSharedRunnersToggle = () => wrapper.findComponent(GlToggle);
const findCcValidationRequiredAlert = () => wrapper.findComponent(CcValidationRequiredAlert); const findCcValidationRequiredAlert = () => wrapper.findComponent(CcValidationRequiredAlert);
const findGenericAlert = () => wrapper.findComponent(GlAlert);
const getToggleValue = () => findSharedRunnersToggle().props('value'); const getToggleValue = () => findSharedRunnersToggle().props('value');
const isToggleDisabled = () => findSharedRunnersToggle().props('disabled'); const isToggleDisabled = () => findSharedRunnersToggle().props('disabled');
...@@ -54,36 +56,39 @@ describe('projects/settings/components/shared_runners', () => { ...@@ -54,36 +56,39 @@ describe('projects/settings/components/shared_runners', () => {
}); });
}); });
it('toggle should not be visible', () => { it('should show the toggle button', () => {
expect(findSharedRunnersToggle().exists()).toBe(false); expect(findSharedRunnersToggle().exists()).toBe(true);
expect(getToggleValue()).toBe(false);
expect(isToggleDisabled()).toBe(false);
}); });
it('credit card validation component should exist', () => { describe('when credit card is unvalidated', () => {
expect(findCcValidationRequiredAlert().exists()).toBe(true); beforeEach(() => {
expect(findCcValidationRequiredAlert().text()).toBe( mockAxios.onPost(TEST_UPDATE_PATH).reply(401, { error: CC_VALIDATION_REQUIRED_ERROR });
SharedRunnersToggleComponent.i18n.REQUIRES_VALIDATION_TEXT, });
);
});
it('credit card alert should be hidden after dismiss', async () => { it('should show credit card validation error on toggle', async () => {
findCcValidationRequiredAlert().vm.$emit('dismiss'); findSharedRunnersToggle().vm.$emit('change', true);
await waitForPromises();
await nextTick(); expect(findCcValidationRequiredAlert().exists()).toBe(true);
expect(findCcValidationRequiredAlert().text()).toBe(
SharedRunnersToggleComponent.i18n.REQUIRES_VALIDATION_TEXT,
);
});
expect(findCcValidationRequiredAlert().exists()).toBe(false); it('should hide credit card alert on dismiss', async () => {
}); findSharedRunnersToggle().vm.$emit('change', true);
await waitForPromises();
describe('when credit card is validated', () => { findCcValidationRequiredAlert().vm.$emit('dismiss');
beforeEach(() => { await nextTick();
findCcValidationRequiredAlert().vm.$emit('verifiedCreditCard');
});
it('should show the toggle button', () => { expect(findCcValidationRequiredAlert().exists()).toBe(false);
expect(findSharedRunnersToggle().exists()).toBe(true);
expect(getToggleValue()).toBe(false);
expect(isToggleDisabled()).toBe(false);
}); });
});
describe('when credit card is validated', () => {
it('should not show credit card alert after toggling on and off', async () => { it('should not show credit card alert after toggling on and off', async () => {
findSharedRunnersToggle().vm.$emit('change', true); findSharedRunnersToggle().vm.$emit('change', true);
await waitForPromises(); await waitForPromises();
...@@ -100,5 +105,22 @@ describe('projects/settings/components/shared_runners', () => { ...@@ -100,5 +105,22 @@ describe('projects/settings/components/shared_runners', () => {
expect(findCcValidationRequiredAlert().exists()).toBe(false); expect(findCcValidationRequiredAlert().exists()).toBe(false);
}); });
}); });
describe('when toggling fails for some other reason', () => {
beforeEach(() => {
mockAxios.onPost(TEST_UPDATE_PATH).reply(500);
});
it('should show a generic alert instead', async () => {
findSharedRunnersToggle().vm.$emit('change', true);
await waitForPromises();
expect(findCcValidationRequiredAlert().exists()).toBe(false);
expect(findGenericAlert().exists()).toBe(true);
expect(findGenericAlert().text()).toBe(
'An error occurred while updating the configuration.',
);
});
});
}); });
}); });
...@@ -10470,6 +10470,9 @@ msgstr "" ...@@ -10470,6 +10470,9 @@ msgstr ""
msgid "CredentialsInventory|SSH Keys" msgid "CredentialsInventory|SSH Keys"
msgstr "" msgstr ""
msgid "Credit card required to be on file in order to create a pipeline"
msgstr ""
msgid "Credit card:" msgid "Credit card:"
msgstr "" msgstr ""
...@@ -33077,6 +33080,9 @@ msgstr "" ...@@ -33077,6 +33080,9 @@ msgstr ""
msgid "Shared runners details" msgid "Shared runners details"
msgstr "" msgstr ""
msgid "Shared runners enabled cannot be enabled until a valid credit card is on file"
msgstr ""
msgid "Shared runners help link" msgid "Shared runners help link"
msgstr "" msgstr ""
......
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