Commit 26a013c9 authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch '331148-error-messages' into 'master'

Update errors for Subscription Activation Alert

See merge request gitlab-org/gitlab!62112
parents 788032b7 0546b49b
...@@ -5,8 +5,12 @@ import { ...@@ -5,8 +5,12 @@ import {
CONNECTIVITY_ERROR, CONNECTIVITY_ERROR,
connectivityErrorAlert, connectivityErrorAlert,
connectivityIssue, connectivityIssue,
generalActivationError, generalActivationErrorMessage,
generalActivationErrorTitle,
howToActivateSubscription, howToActivateSubscription,
INVALID_CODE_ERROR,
invalidActivationCode,
supportLink,
} from '../constants'; } from '../constants';
export const troubleshootingHelpLink = helpPagePath('user/admin_area/license.html', { export const troubleshootingHelpLink = helpPagePath('user/admin_area/license.html', {
...@@ -20,11 +24,14 @@ export default { ...@@ -20,11 +24,14 @@ export default {
connectivityIssueTitle: connectivityIssue, connectivityIssueTitle: connectivityIssue,
connectivityIssueSubtitle: connectivityErrorAlert.subtitle, connectivityIssueSubtitle: connectivityErrorAlert.subtitle,
connectivityIssueHelpText: connectivityErrorAlert.helpText, connectivityIssueHelpText: connectivityErrorAlert.helpText,
generalActivationError, generalActivationErrorMessage,
generalActivationErrorTitle,
howToActivateSubscription, howToActivateSubscription,
invalidActivationCode,
}, },
links: { links: {
subscriptionActivationHelpLink, subscriptionActivationHelpLink,
supportLink,
troubleshootingHelpLink, troubleshootingHelpLink,
}, },
components: { components: {
...@@ -40,20 +47,26 @@ export default { ...@@ -40,20 +47,26 @@ export default {
}, },
}, },
computed: { computed: {
hasConnectivityIssue() { hasConnectivityIssueError() {
return this.error === CONNECTIVITY_ERROR; return this.error === CONNECTIVITY_ERROR;
}, },
hasError() {
return this.error;
},
hasGeneralError() { hasGeneralError() {
return this.error && !this.hasConnectivityIssue; return ![CONNECTIVITY_ERROR, INVALID_CODE_ERROR].includes(this.error);
},
hasInvalidCodeError() {
return this.error === INVALID_CODE_ERROR;
}, },
}, },
}; };
</script> </script>
<template> <template>
<div> <div v-if="hasError" data-testid="root">
<gl-alert <gl-alert
v-if="hasConnectivityIssue" v-if="hasConnectivityIssueError"
variant="danger" variant="danger"
:title="$options.i18n.connectivityIssueTitle" :title="$options.i18n.connectivityIssueTitle"
:dismissible="false" :dismissible="false"
...@@ -80,19 +93,37 @@ export default { ...@@ -80,19 +93,37 @@ export default {
</template> </template>
</gl-sprintf> </gl-sprintf>
</gl-alert> </gl-alert>
<gl-alert
v-if="hasInvalidCodeError"
variant="danger"
:title="$options.i18n.generalActivationErrorTitle"
:dismissible="false"
data-testid="invalid-activation-error-alert"
>
<gl-sprintf :message="$options.i18n.invalidActivationCode">
<template #link="{ content }">
<gl-link :href="$options.links.subscriptionActivationHelpLink" target="_blank">{{
content
}}</gl-link>
</template>
</gl-sprintf>
</gl-alert>
<gl-alert <gl-alert
v-if="hasGeneralError" v-if="hasGeneralError"
variant="danger" variant="danger"
:title="$options.i18n.generalActivationError" :title="$options.i18n.generalActivationErrorTitle"
:dismissible="false" :dismissible="false"
data-testid="general-error-alert" data-testid="general-error-alert"
> >
<gl-sprintf :message="$options.i18n.howToActivateSubscription"> <gl-sprintf :message="$options.i18n.generalActivationErrorMessage">
<template #link="{ content }"> <template #activationLink="{ content }">
<gl-link :href="$options.links.subscriptionActivationHelpLink" target="_blank">{{ <gl-link :href="$options.links.subscriptionActivationHelpLink" target="_blank">{{
content content
}}</gl-link> }}</gl-link>
</template> </template>
<template #supportLink="{ content }">
<gl-link :href="$options.links.supportLink" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf> </gl-sprintf>
</gl-alert> </gl-alert>
</div> </div>
......
...@@ -12,6 +12,8 @@ import validation from '~/vue_shared/directives/validation'; ...@@ -12,6 +12,8 @@ import validation from '~/vue_shared/directives/validation';
import { import {
activateLabel, activateLabel,
fieldRequiredMessage, fieldRequiredMessage,
INVALID_CODE_ERROR,
INVALID_CODE_ERROR_MESSAGE,
subscriptionActivationForm, subscriptionActivationForm,
subscriptionQueries, subscriptionQueries,
} from '../constants'; } from '../constants';
...@@ -103,6 +105,9 @@ export default { ...@@ -103,6 +105,9 @@ export default {
const errors = getErrorsAsData(res); const errors = getErrorsAsData(res);
if (errors.length) { if (errors.length) {
const [error] = errors; const [error] = errors;
if (error.includes(INVALID_CODE_ERROR_MESSAGE)) {
throw new Error(INVALID_CODE_ERROR);
}
throw new Error(error); throw new Error(error);
} }
this.$emit(SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT); this.$emit(SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT);
......
...@@ -115,10 +115,18 @@ export const buySubscriptionCard = { ...@@ -115,10 +115,18 @@ export const buySubscriptionCard = {
buttonLabel: s__('SuperSonics|Buy subscription'), buttonLabel: s__('SuperSonics|Buy subscription'),
}; };
export const INVALID_CODE_ERROR_MESSAGE = 'invalid activation code';
export const CONNECTIVITY_ERROR = 'CONNECTIVITY_ERROR'; export const CONNECTIVITY_ERROR = 'CONNECTIVITY_ERROR';
export const generalActivationError = s__( export const INVALID_CODE_ERROR = 'INVALID_CODE_ERROR';
export const generalActivationErrorTitle = s__(
'SuperSonics|An error occurred while activating your subscription.', 'SuperSonics|An error occurred while activating your subscription.',
); );
export const generalActivationErrorMessage = s__(
'SuperSonics|You can learn more about %{activationLinkStart}activating your subscription%{activationLinkEnd}. If you need further assistance, please %{supportLinkStart}contact GitLab Support%{supportLinkEnd}.',
);
export const invalidActivationCode = s__(
'SuperSonics|The activation code is not valid. Please make sure to copy it exactly from the Customers Portal or confirmation email. Learn more about %{linkStart}activating your subscription%{linkEnd}.',
);
export const connectivityErrorAlert = { export const connectivityErrorAlert = {
subtitle: s__( subtitle: s__(
'SuperSonics|To activate your subscription, connect to GitLab servers through the %{linkStart}Cloud Licensing%{linkEnd} service, a hassle-free way to manage your subscription.', 'SuperSonics|To activate your subscription, connect to GitLab servers through the %{linkStart}Cloud Licensing%{linkEnd} service, a hassle-free way to manage your subscription.',
...@@ -127,3 +135,4 @@ export const connectivityErrorAlert = { ...@@ -127,3 +135,4 @@ export const connectivityErrorAlert = {
'SuperSonics|Get help for the most common connectivity issues by %{linkStart}troubleshooting the activation code%{linkEnd}.', 'SuperSonics|Get help for the most common connectivity issues by %{linkStart}troubleshooting the activation code%{linkEnd}.',
), ),
}; };
export const supportLink = 'https://about.gitlab.com/support/#contact-support';
import { GlLink, GlSprintf } from '@gitlab/ui'; import { GlLink, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { import SubscriptionActivationErrors, {
subscriptionActivationHelpLink, subscriptionActivationHelpLink,
troubleshootingHelpLink, troubleshootingHelpLink,
} from 'ee/pages/admin/cloud_licenses/components/subscription_activation_card.vue'; } from 'ee/pages/admin/cloud_licenses/components/subscription_activation_errors.vue';
import SubscriptionActivationErrors from 'ee/pages/admin/cloud_licenses/components/subscription_activation_errors.vue';
import { import {
CONNECTIVITY_ERROR, CONNECTIVITY_ERROR,
connectivityIssue, generalActivationErrorMessage,
generalActivationError, generalActivationErrorTitle,
invalidActivationCode,
INVALID_CODE_ERROR,
supportLink,
} from 'ee/pages/admin/cloud_licenses/constants'; } from 'ee/pages/admin/cloud_licenses/constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import { extendedWrapper } from 'helpers/vue_test_utils_helper';
...@@ -17,6 +19,8 @@ describe('SubscriptionActivationErrors', () => { ...@@ -17,6 +19,8 @@ describe('SubscriptionActivationErrors', () => {
const findConnectivityErrorAlert = () => wrapper.findByTestId('connectivity-error-alert'); const findConnectivityErrorAlert = () => wrapper.findByTestId('connectivity-error-alert');
const findGeneralErrorAlert = () => wrapper.findByTestId('general-error-alert'); const findGeneralErrorAlert = () => wrapper.findByTestId('general-error-alert');
const findInvalidActivationCode = () => wrapper.findByTestId('invalid-activation-error-alert');
const findRoot = () => wrapper.findByTestId('root');
const createComponent = ({ props = {} } = {}) => { const createComponent = ({ props = {} } = {}) => {
wrapper = extendedWrapper( wrapper = extendedWrapper(
...@@ -33,26 +37,54 @@ describe('SubscriptionActivationErrors', () => { ...@@ -33,26 +37,54 @@ describe('SubscriptionActivationErrors', () => {
wrapper.destroy(); wrapper.destroy();
}); });
describe('connectivity error', () => { describe('with no error', () => {
beforeEach(() => { beforeEach(() => {
createComponent({ props: { error: CONNECTIVITY_ERROR } }); createComponent();
}); });
it('shows the alert', () => { it('should not render the component', () => {
expect(findConnectivityErrorAlert().props('title')).toBe(connectivityIssue); expect(findRoot().exists()).toBe(false);
});
});
describe('connectivity error', () => {
beforeEach(() => {
createComponent({ props: { error: CONNECTIVITY_ERROR } });
}); });
it('shows some help links', () => { it('shows some help links', () => {
const alert = findConnectivityErrorAlert(); const alert = findConnectivityErrorAlert();
expect(alert.findAllComponents(GlLink).at(0).props('href')).toBe( expect(alert.findAllComponents(GlLink).at(0).attributes('href')).toBe(
subscriptionActivationHelpLink, subscriptionActivationHelpLink,
); );
expect(alert.findAllComponents(GlLink).at(1).props('href')).toBe(troubleshootingHelpLink); expect(alert.findAllComponents(GlLink).at(1).attributes('href')).toBe(
troubleshootingHelpLink,
);
}); });
it('does not show other alerts', () => { it('does not show other alerts', () => {
expect(findGeneralErrorAlert().exists()).toBe(false); expect(findGeneralErrorAlert().exists()).toBe(false);
expect(findInvalidActivationCode().exists()).toBe(false);
});
});
describe('invalid activation code error', () => {
beforeEach(() => {
createComponent({ props: { error: INVALID_CODE_ERROR } });
});
it('shows the alert', () => {
expect(findInvalidActivationCode().attributes('title')).toBe(generalActivationErrorTitle);
});
it('shows a text to help the user', () => {
expect(findInvalidActivationCode().text()).toMatchInterpolatedText(invalidActivationCode);
});
it('does not show other alerts', () => {
expect(findConnectivityErrorAlert().exists()).toBe(false);
expect(findGeneralErrorAlert().exists()).toBe(false);
}); });
}); });
...@@ -62,21 +94,26 @@ describe('SubscriptionActivationErrors', () => { ...@@ -62,21 +94,26 @@ describe('SubscriptionActivationErrors', () => {
}); });
it('shows a general error alert', () => { it('shows a general error alert', () => {
expect(findGeneralErrorAlert().props('title')).toBe(generalActivationError); expect(findGeneralErrorAlert().props('title')).toBe(generalActivationErrorTitle);
}); });
it('shows a help link', () => { it('shows some help links', () => {
expect(findGeneralErrorAlert().findComponent(GlLink).props('href')).toBe( const alert = findGeneralErrorAlert();
expect(alert.findAllComponents(GlLink).at(0).attributes('href')).toBe(
subscriptionActivationHelpLink, subscriptionActivationHelpLink,
); );
expect(alert.findAllComponents(GlLink).at(1).attributes('href')).toBe(supportLink);
}); });
it('shows a a text to help the user', () => { it('shows a text to help the user', () => {
expect(findGeneralErrorAlert().text()).toBe('Learn how to activate your subscription.'); expect(findGeneralErrorAlert().text()).toMatchInterpolatedText(generalActivationErrorMessage);
}); });
it('does not show the connectivity alert', () => { it('does not show the connectivity alert', () => {
expect(findConnectivityErrorAlert().exists()).toBe(false); expect(findConnectivityErrorAlert().exists()).toBe(false);
expect(findInvalidActivationCode().exists()).toBe(false);
}); });
}); });
}); });
...@@ -8,6 +8,7 @@ import SubscriptionActivationForm, { ...@@ -8,6 +8,7 @@ import SubscriptionActivationForm, {
import { import {
CONNECTIVITY_ERROR, CONNECTIVITY_ERROR,
fieldRequiredMessage, fieldRequiredMessage,
INVALID_CODE_ERROR,
subscriptionQueries, subscriptionQueries,
} from 'ee/pages/admin/cloud_licenses/constants'; } from 'ee/pages/admin/cloud_licenses/constants';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
...@@ -179,6 +180,24 @@ describe('CloudLicenseApp', () => { ...@@ -179,6 +180,24 @@ describe('CloudLicenseApp', () => {
}); });
}); });
describe('when the mutation is not successful with invalid activation code error', () => {
const mutationMock = jest
.fn()
.mockResolvedValue(activateLicenseMutationResponse.INVALID_CODE_ERROR);
beforeEach(async () => {
createComponentWithApollo({ mutationMock });
await findActivationCodeInput().vm.$emit('input', fakeActivationCode);
await findAgreementCheckbox().vm.$emit('input', true);
findActivateSubscriptionForm().vm.$emit('submit', createFakeEvent());
});
it('emits an failure event with a connectivity error payload', () => {
expect(wrapper.emitted(SUBSCRIPTION_ACTIVATION_FAILURE_EVENT)).toEqual([
[INVALID_CODE_ERROR],
]);
});
});
describe('when the mutation request fails', () => { describe('when the mutation request fails', () => {
const mutationMock = jest.fn().mockRejectedValue(activateLicenseMutationResponse.FAILURE); const mutationMock = jest.fn().mockRejectedValue(activateLicenseMutationResponse.FAILURE);
beforeEach(() => { beforeEach(() => {
......
...@@ -81,6 +81,15 @@ export const activateLicenseMutationResponse = { ...@@ -81,6 +81,15 @@ export const activateLicenseMutationResponse = {
}, },
}, },
}, },
INVALID_CODE_ERROR: {
data: {
gitlabSubscriptionActivate: {
license: null,
errors: ['invalid activation code'],
__typename: 'GitlabSubscriptionActivatePayload',
},
},
},
ERRORS_AS_DATA: { ERRORS_AS_DATA: {
data: { data: {
gitlabSubscriptionActivate: { gitlabSubscriptionActivate: {
......
...@@ -31662,6 +31662,9 @@ msgstr "" ...@@ -31662,6 +31662,9 @@ msgstr ""
msgid "SuperSonics|Sync subscription details" msgid "SuperSonics|Sync subscription details"
msgstr "" msgstr ""
msgid "SuperSonics|The activation code is not valid. Please make sure to copy it exactly from the Customers Portal or confirmation email. Learn more about %{linkStart}activating your subscription%{linkEnd}."
msgstr ""
msgid "SuperSonics|The subscription details synced successfully." msgid "SuperSonics|The subscription details synced successfully."
msgstr "" msgstr ""
...@@ -31698,6 +31701,9 @@ msgstr "" ...@@ -31698,6 +31701,9 @@ msgstr ""
msgid "SuperSonics|Valid From" msgid "SuperSonics|Valid From"
msgstr "" msgstr ""
msgid "SuperSonics|You can learn more about %{activationLinkStart}activating your subscription%{activationLinkEnd}. If you need further assistance, please %{supportLinkStart}contact GitLab Support%{supportLinkEnd}."
msgstr ""
msgid "SuperSonics|You can no longer sync your subscription details with GitLab. Get help for the most common connectivity issues by %{connectivityHelpLinkStart}troubleshooting the activation code%{connectivityHelpLinkEnd}." msgid "SuperSonics|You can no longer sync your subscription details with GitLab. Get help for the most common connectivity issues by %{connectivityHelpLinkStart}troubleshooting the activation code%{connectivityHelpLinkEnd}."
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