Commit eae93ac8 authored by Savas Vedova's avatar Savas Vedova

Merge branch 'ag-328246-activation-modal-components-continue' into 'master'

Subscription Activation Modal: Create Modal Component

See merge request gitlab-org/gitlab!60819
parents 1397455c b1a21227
<script>
import { GlAlert, GlCard, GlLink, GlSprintf } from '@gitlab/ui';
import { GlCard, GlLink, GlSprintf } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import {
activateSubscription,
CONNECTIVITY_ERROR,
connectivityErrorAlert,
connectivityIssue,
howToActivateSubscription,
uploadLegacyLicense,
} from '../constants';
import { activateSubscription, howToActivateSubscription, uploadLegacyLicense } from '../constants';
import SubscriptionActivationErrors from './subscription_activation_errors.vue';
import SubscriptionActivationForm from './subscription_activation_form.vue';
export const adminLicenseUrl = helpPagePath('/user/admin_area/license');
export const troubleshootingHelpLink = helpPagePath('user/admin_area/license.html#troubleshooting');
export const subscriptionActivationHelpLink = helpPagePath('user/admin_area/license.html');
export default {
name: 'SubscriptionActivationCard',
i18n: {
activateSubscription,
connectivityIssueTitle: connectivityIssue,
connectivityIssueSubtitle: connectivityErrorAlert.subtitle,
connectivityIssueHelpText: connectivityErrorAlert.helpText,
howToActivateSubscription,
uploadLegacyLicense,
},
components: {
GlAlert,
GlCard,
GlLink,
GlSprintf,
SubscriptionActivationErrors,
SubscriptionActivationForm,
},
inject: ['licenseUploadPath'],
links: {
adminLicenseUrl,
subscriptionActivationHelpLink,
troubleshootingHelpLink,
},
data() {
return {
error: null,
};
},
computed: {
hasConnectivityIssue() {
return this.error === CONNECTIVITY_ERROR;
},
},
methods: {
handleFormActivationFailure(error) {
this.error = error;
......@@ -63,32 +45,8 @@ export default {
{{ $options.i18n.activateSubscription }}
</h5>
</template>
<div
v-if="hasConnectivityIssue"
class="gl-p-5 gl-border-b-1 gl-border-gray-100 gl-border-b-solid"
>
<gl-alert variant="danger" :title="$options.i18n.connectivityIssueTitle" :dismissible="false">
<gl-sprintf :message="$options.i18n.connectivityIssueSubtitle">
<template #link="{ content }">
<gl-link
:href="$options.links.subscriptionActivationHelpLink"
target="_blank"
class="gl-text-decoration-none!"
>{{ content }}
</gl-link>
</template>
</gl-sprintf>
<gl-sprintf :message="$options.i18n.connectivityIssueHelpText">
<template #link="{ content }">
<gl-link
:href="$options.links.troubleshootingHelpLink"
target="_blank"
class="gl-text-decoration-none!"
>{{ content }}
</gl-link>
</template>
</gl-sprintf>
</gl-alert>
<div v-if="error" class="gl-p-5 gl-border-b-1 gl-border-gray-100 gl-border-b-solid">
<subscription-activation-errors class="mb-4" :error="error" />
</div>
<p class="gl-mb-0 gl-px-5 gl-pt-5">
<gl-sprintf :message="$options.i18n.howToActivateSubscription">
......
<script>
import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import {
CONNECTIVITY_ERROR,
connectivityErrorAlert,
connectivityIssue,
generalActivationError,
howToActivateSubscription,
} from '../constants';
export const troubleshootingHelpLink = helpPagePath('user/admin_area/license.html', {
anchor: 'troubleshooting',
});
export const subscriptionActivationHelpLink = helpPagePath('user/admin_area/license.html');
export default {
name: 'SubscriptionActivationErrors',
i18n: {
connectivityIssueTitle: connectivityIssue,
connectivityIssueSubtitle: connectivityErrorAlert.subtitle,
connectivityIssueHelpText: connectivityErrorAlert.helpText,
generalActivationError,
howToActivateSubscription,
},
links: {
subscriptionActivationHelpLink,
troubleshootingHelpLink,
},
components: {
GlAlert,
GlLink,
GlSprintf,
},
props: {
error: {
type: String,
required: false,
default: '',
},
},
computed: {
hasConnectivityIssue() {
return this.error === CONNECTIVITY_ERROR;
},
hasGeneralError() {
return this.error && !this.hasConnectivityIssue;
},
},
};
</script>
<template>
<div>
<gl-alert
v-if="hasConnectivityIssue"
variant="danger"
:title="$options.i18n.connectivityIssueTitle"
:dismissible="false"
data-testid="connectivity-error-alert"
>
<gl-sprintf :message="$options.i18n.connectivityIssueSubtitle">
<template #link="{ content }">
<gl-link
:href="$options.links.subscriptionActivationHelpLink"
target="_blank"
class="gl-text-decoration-none!"
>{{ content }}
</gl-link>
</template>
</gl-sprintf>
<gl-sprintf :message="$options.i18n.connectivityIssueHelpText">
<template #link="{ content }">
<gl-link
:href="$options.links.troubleshootingHelpLink"
target="_blank"
class="gl-text-decoration-none!"
>{{ content }}
</gl-link>
</template>
</gl-sprintf>
</gl-alert>
<gl-alert
v-if="hasGeneralError"
variant="danger"
:title="$options.i18n.generalActivationError"
:dismissible="false"
data-testid="general-error-alert"
>
<gl-sprintf :message="$options.i18n.howToActivateSubscription">
<template #link="{ content }">
<gl-link :href="$options.links.adminLicenseUrl" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</gl-alert>
</div>
</template>
......@@ -30,6 +30,7 @@ const getErrorsAsData = ({
}) => errors;
export const SUBSCRIPTION_ACTIVATION_FAILURE_EVENT = 'subscription-activation-failure';
export const SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT = 'subscription-activation-success';
export default {
name: 'CloudLicenseSubscriptionActivationForm',
......@@ -52,7 +53,14 @@ export default {
directives: {
validation: validation(),
},
emits: [SUBSCRIPTION_ACTIVATION_FAILURE_EVENT],
props: {
hideSubmitButton: {
type: Boolean,
required: false,
default: false,
},
},
emits: [SUBSCRIPTION_ACTIVATION_FAILURE_EVENT, SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT],
data() {
const form = {
state: false,
......@@ -119,6 +127,7 @@ export default {
const [error] = errors;
throw new Error(error);
}
this.$emit(SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT);
})
.catch((error) => {
this.$emit(SUBSCRIPTION_ACTIVATION_FAILURE_EVENT, error.message);
......@@ -172,6 +181,7 @@ export default {
</gl-form-group>
<gl-button
v-if="!hideSubmitButton"
:loading="isRequestingActivation"
category="primary"
class="gl-mt-6 js-no-auto-disable"
......
<script>
import { GlModal } from '@gitlab/ui';
import { __ } from '~/locale';
import {
activateLabel,
activateSubscription,
subscriptionActivationInsertCode,
} from '../constants';
import SubscriptionActivationErrors from './subscription_activation_errors.vue';
import SubscriptionActivationForm from './subscription_activation_form.vue';
export default {
actionCancel: { text: __('Cancel') },
actionPrimary: {
text: activateLabel,
},
bodyText: subscriptionActivationInsertCode,
title: activateSubscription,
name: 'SubscriptionActivationModal',
components: {
GlModal,
SubscriptionActivationErrors,
SubscriptionActivationForm,
},
props: {
modalId: {
type: String,
required: true,
},
},
data() {
return {
error: null,
};
},
methods: {
handleActivationFailure(error) {
this.error = error;
},
handleActivationSuccess() {
this.$refs.modal.hide();
},
handlePrimary() {
this.$refs.form.submit();
},
removeError() {
this.error = null;
},
},
};
</script>
<template>
<gl-modal
ref="modal"
:modal-id="modalId"
:title="$options.title"
:action-cancel="$options.actionCancel"
:action-primary="$options.actionPrimary"
@primary.prevent="handlePrimary"
@hidden="removeError"
>
<subscription-activation-errors v-if="error" class="mb-4" :error="error" />
<p>{{ $options.bodyText }}</p>
<subscription-activation-form
ref="form"
:hide-submit-button="true"
@subscription-activation-failure="handleActivationFailure"
@subscription-activation-success="handleActivationSuccess"
/>
</gl-modal>
</template>
......@@ -113,6 +113,9 @@ export const buySubscriptionCard = {
};
export const CONNECTIVITY_ERROR = 'CONNECTIVITY_ERROR';
export const generalActivationError = s__(
'SuperSonics|An error occurred while activating your subscription.',
);
export const connectivityErrorAlert = {
subtitle: s__(
'CloudLicense|To activate your subscription, connect to GitLab servers through the %{linkStart}Cloud Sync service%{linkEnd}, a hassle-free way to manage your subscription.',
......
import { GlAlert, GlCard, GlLink, GlSprintf } from '@gitlab/ui';
import { GlCard } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import SubscriptionActivationCard, {
subscriptionActivationHelpLink,
troubleshootingHelpLink,
} from 'ee/pages/admin/cloud_licenses/components/subscription_activation_card.vue';
import SubscriptionActivationCard from 'ee/pages/admin/cloud_licenses/components/subscription_activation_card.vue';
import SubscriptionActivationErrors from 'ee/pages/admin/cloud_licenses/components/subscription_activation_errors.vue';
import SubscriptionActivationForm, {
SUBSCRIPTION_ACTIVATION_FAILURE_EVENT,
} from 'ee/pages/admin/cloud_licenses/components/subscription_activation_form.vue';
......@@ -14,8 +12,9 @@ describe('CloudLicenseApp', () => {
let wrapper;
const licenseUploadPath = 'license/upload';
const findConnectivityErrorAlert = () => wrapper.findComponent(GlAlert);
const findSubscriptionActivationForm = () => wrapper.findComponent(SubscriptionActivationForm);
const findSubscriptionActivationErrors = () =>
wrapper.findComponent(SubscriptionActivationErrors);
const findUploadLink = () => wrapper.findByTestId('upload-license-link');
const createComponent = ({ props = {}, stubs = {}, provide = {} } = {}) => {
......@@ -46,7 +45,7 @@ describe('CloudLicenseApp', () => {
});
it('does not show any alert', () => {
expect(findConnectivityErrorAlert().exists()).toBe(false);
expect(findSubscriptionActivationErrors().exists()).toBe(false);
});
describe('with an upload legacy license link', () => {
......@@ -76,7 +75,7 @@ describe('CloudLicenseApp', () => {
describe('when the forms emits a connectivity error', () => {
beforeEach(() => {
createComponent({ stubs: { GlSprintf } });
createComponent();
findSubscriptionActivationForm().vm.$emit(
SUBSCRIPTION_ACTIVATION_FAILURE_EVENT,
CONNECTIVITY_ERROR,
......@@ -84,14 +83,11 @@ describe('CloudLicenseApp', () => {
});
it('shows an alert component', () => {
expect(findConnectivityErrorAlert().exists()).toBe(true);
expect(findSubscriptionActivationErrors().exists()).toBe(true);
});
it('shows some help links', () => {
const alert = findConnectivityErrorAlert();
expect(alert.findAll(GlLink).at(0).attributes('href')).toBe(subscriptionActivationHelpLink);
expect(alert.findAll(GlLink).at(1).attributes('href')).toBe(troubleshootingHelpLink);
it('passes the correct error to the component', () => {
expect(findSubscriptionActivationErrors().props('error')).toBe(CONNECTIVITY_ERROR);
});
});
});
import { GlLink, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import {
subscriptionActivationHelpLink,
troubleshootingHelpLink,
} from 'ee/pages/admin/cloud_licenses/components/subscription_activation_card.vue';
import SubscriptionActivationErrors from 'ee/pages/admin/cloud_licenses/components/subscription_activation_errors.vue';
import {
CONNECTIVITY_ERROR,
connectivityIssue,
generalActivationError,
} from 'ee/pages/admin/cloud_licenses/constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
describe('SubscriptionActivationErrors', () => {
let wrapper;
const findConnectivityErrorAlert = () => wrapper.findByTestId('connectivity-error-alert');
const findGeneralErrorAlert = () => wrapper.findByTestId('general-error-alert');
const createComponent = ({ props = {} } = {}) => {
wrapper = extendedWrapper(
shallowMount(SubscriptionActivationErrors, {
propsData: {
...props,
},
stubs: { GlSprintf },
}),
);
};
afterEach(() => {
wrapper.destroy();
});
describe('connectivity error', () => {
beforeEach(() => {
createComponent({ props: { error: CONNECTIVITY_ERROR } });
});
it('shows the alert', () => {
expect(findConnectivityErrorAlert().props('title')).toBe(connectivityIssue);
});
it('shows some help links', () => {
const alert = findConnectivityErrorAlert();
expect(alert.findAll(GlLink).at(0).props('href')).toBe(subscriptionActivationHelpLink);
expect(alert.findAll(GlLink).at(1).props('href')).toBe(troubleshootingHelpLink);
});
it('does not show other alerts', () => {
expect(findGeneralErrorAlert().exists()).toBe(false);
});
});
describe('general error', () => {
beforeEach(() => {
createComponent({ props: { error: 'A fake error' } });
});
it('shows a general error alert', () => {
expect(findGeneralErrorAlert().props('title')).toBe(generalActivationError);
});
it('shows a a text to help the user', () => {
expect(findGeneralErrorAlert().text()).toBe('Learn how to activate your subscription.');
});
it('does not show the connectivity alert', () => {
expect(findConnectivityErrorAlert().exists()).toBe(false);
});
});
});
......@@ -3,6 +3,7 @@ import { createLocalVue, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import SubscriptionActivationForm, {
SUBSCRIPTION_ACTIVATION_FAILURE_EVENT,
SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT,
} from 'ee/pages/admin/cloud_licenses/components/subscription_activation_form.vue';
import {
CONNECTIVITY_ERROR,
......@@ -135,6 +136,10 @@ describe('CloudLicenseApp', () => {
},
});
});
it('emits a successful event', () => {
expect(wrapper.emitted(SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT)).toEqual([[]]);
});
});
describe('when the mutation is not successful', () => {
......
import { GlModal } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import SubscriptionActivationErrors from 'ee/pages/admin/cloud_licenses/components/subscription_activation_errors.vue';
import SubscriptionActivationForm, {
SUBSCRIPTION_ACTIVATION_FAILURE_EVENT,
SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT,
} from 'ee/pages/admin/cloud_licenses/components/subscription_activation_form.vue';
import SubscriptionActivationModal from 'ee/pages/admin/cloud_licenses/components/subscription_activation_modal.vue';
import {
activateSubscription,
CONNECTIVITY_ERROR,
subscriptionActivationInsertCode,
} from 'ee/pages/admin/cloud_licenses/constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { preventDefault } from '../../test_helpers';
describe('SubscriptionActivationModal', () => {
let wrapper;
const modalId = 'fake-modal-id';
const findGlModal = () => wrapper.findComponent(GlModal);
const findSubscriptionActivationErrors = () =>
wrapper.findComponent(SubscriptionActivationErrors);
const findSubscriptionActivationForm = () => wrapper.findComponent(SubscriptionActivationForm);
const createComponent = ({ props = {}, stubs = {} } = {}) => {
wrapper = extendedWrapper(
shallowMount(SubscriptionActivationModal, {
propsData: {
modalId,
...props,
},
stubs,
}),
);
};
afterEach(() => {
wrapper.destroy();
});
describe('idle state', () => {
beforeEach(() => {
createComponent();
});
it('has an id', () => {
expect(findGlModal().attributes('modalid')).toBe(modalId);
});
it('shows a description text', () => {
expect(wrapper.text()).toContain(subscriptionActivationInsertCode);
});
it('shows a title', () => {
expect(findGlModal().attributes('title')).toBe(activateSubscription);
});
it('shows the subscription activation form', () => {
expect(findSubscriptionActivationForm().exists()).toBe(true);
});
it('hides the form default button', () => {
expect(findSubscriptionActivationForm().props('hideSubmitButton')).toBe(true);
});
it('does not show any error', () => {
expect(findSubscriptionActivationErrors().exists()).toBe(false);
});
});
describe('subscription activation', () => {
const fakeEvent = 'fake-modal-event';
const hiddenEven = 'hidden';
describe('when submitting the form', () => {
beforeEach(() => {
createComponent();
jest
.spyOn(wrapper.vm, 'handlePrimary')
.mockImplementation(() => wrapper.vm.$emit(fakeEvent));
findGlModal().vm.$emit('primary', { preventDefault });
});
it('emits the correct event', () => {
expect(wrapper.emitted(fakeEvent)).toEqual([[]]);
});
});
describe('successful activation', () => {
beforeEach(() => {
createComponent({ stubs: { GlModal } });
jest
.spyOn(wrapper.vm.$refs.modal, 'hide')
.mockImplementation(() => wrapper.vm.$emit(hiddenEven));
findSubscriptionActivationForm().vm.$emit(SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT);
});
it('it emits a hidden event', () => {
expect(wrapper.emitted(hiddenEven)).toEqual([[]]);
});
});
describe('failing activation', () => {
beforeEach(() => {
createComponent();
findSubscriptionActivationForm().vm.$emit(
SUBSCRIPTION_ACTIVATION_FAILURE_EVENT,
CONNECTIVITY_ERROR,
);
});
it('passes the correct props', () => {
expect(findSubscriptionActivationErrors().props('error')).toBe(CONNECTIVITY_ERROR);
});
it('resets the component state when closing', async () => {
await findGlModal().vm.$emit('hidden');
expect(findSubscriptionActivationErrors().exists()).toBe(false);
});
});
});
});
......@@ -31085,6 +31085,9 @@ msgstr ""
msgid "SuperSonics|Activated on"
msgstr ""
msgid "SuperSonics|An error occurred while activating your subscription."
msgstr ""
msgid "SuperSonics|Expires on"
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