Commit c76a06d2 authored by Angelo Gulina's avatar Angelo Gulina Committed by Mark Florian

Fix design inconsistency

Update copy and styles for CI Minutes purchase flow
parent dc25a083
...@@ -41,7 +41,7 @@ export default { ...@@ -41,7 +41,7 @@ export default {
<template> <template>
<div class="checkout gl-display-flex gl-flex-direction-column gl-align-items-center"> <div class="checkout gl-display-flex gl-flex-direction-column gl-align-items-center">
<div class="flash-container"></div> <div class="flash-container"></div>
<h2 class="gl-mt-6 gl-mb-7 gl-mb-lg-5">{{ $options.i18n.checkout }}</h2> <h2 class="gl-align-self-start gl-mt-6 gl-mb-7 gl-mb-lg-5">{{ $options.i18n.checkout }}</h2>
<addon-purchase-details :plan="plan" /> <addon-purchase-details :plan="plan" />
<billing-address /> <billing-address />
<payment-method /> <payment-method />
......
...@@ -7,7 +7,7 @@ import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql'; ...@@ -7,7 +7,7 @@ import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql';
import Step from 'ee/vue_shared/purchase_flow/components/step.vue'; import Step from 'ee/vue_shared/purchase_flow/components/step.vue';
import { GENERAL_ERROR_MESSAGE } from 'ee/vue_shared/purchase_flow/constants'; import { GENERAL_ERROR_MESSAGE } from 'ee/vue_shared/purchase_flow/constants';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { sprintf, s__, formatNumber } from '~/locale'; import { n__, s__, sprintf, formatNumber } from '~/locale';
import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
export default { export default {
...@@ -45,14 +45,17 @@ export default { ...@@ -45,14 +45,17 @@ export default {
return this.quantity * CI_MINUTES_PER_PACK; return this.quantity * CI_MINUTES_PER_PACK;
}, },
summaryCiMinutesQuantityText() { summaryCiMinutesQuantityText() {
return sprintf(this.$options.i18n.summaryCiMinutesQuantity, { return n__('Checkout|%d CI minute pack', 'Checkout|%d CI minute packs', this.quantity);
quantity: this.quantity,
});
}, },
ciMinutesQuantityText() { ciMinutesQuantityText() {
return sprintf(this.$options.i18n.ciMinutesQuantityText, { return sprintf(
totalCiMinutes: formatNumber(this.totalCiMinutes), n__(
}); 'Checkout|%{totalCiMinutes} CI minute',
'Checkout|%{totalCiMinutes} CI minutes',
this.totalCiMinutes,
),
{ totalCiMinutes: formatNumber(this.totalCiMinutes) },
);
}, },
summaryCiMinutesTotal() { summaryCiMinutesTotal() {
return sprintf(this.$options.i18n.summaryCiMinutesTotal, { return sprintf(this.$options.i18n.summaryCiMinutesTotal, {
...@@ -77,13 +80,12 @@ export default { ...@@ -77,13 +80,12 @@ export default {
i18n: { i18n: {
stepTitle: s__('Checkout|Purchase details'), stepTitle: s__('Checkout|Purchase details'),
nextStepButtonText: s__('Checkout|Continue to billing'), nextStepButtonText: s__('Checkout|Continue to billing'),
ciMinutesPacksLabel: s__('Checkout|CI minute packs'), ciMinutesLabel: s__('Checkout|CI minute pack'),
ciMinutesAlertText: s__( ciMinutesAlertText: s__(
"Checkout|CI minute packs are only used after you've used your subscription's monthly quota. The additional minutes will roll over month to month and are valid for one year.", "Checkout|CI minute packs are only used after you've used your subscription's monthly quota. The additional minutes will roll over month to month and are valid for one year.",
), ),
ciMinutesPacksQuantityFormula: s__('Checkout|x 1,000 minutes per pack = %{strong}'), ciMinutesPacksQuantityFormula: s__('Checkout|x 1,000 minutes per pack = %{strong}'),
ciMinutesQuantityText: s__('Checkout|%{totalCiMinutes} CI minutes'), ciMinutesQuantityText: s__('Checkout|%{totalCiMinutes} CI minutes'),
summaryCiMinutesQuantity: s__('Checkout|%{quantity} CI minute packs'),
summaryCiMinutesTotal: s__('Checkout|Total minutes: %{quantity}'), summaryCiMinutesTotal: s__('Checkout|Total minutes: %{quantity}'),
}, },
stepId: STEPS[0].id, stepId: STEPS[0].id,
...@@ -101,8 +103,10 @@ export default { ...@@ -101,8 +103,10 @@ export default {
<gl-alert variant="info" class="gl-mb-3" :dismissible="false"> <gl-alert variant="info" class="gl-mb-3" :dismissible="false">
{{ $options.i18n.ciMinutesAlertText }} {{ $options.i18n.ciMinutesAlertText }}
</gl-alert> </gl-alert>
<label for="quantity">{{ $options.i18n.ciMinutesPacksLabel }}</label> <label class="gl-mt-3" for="quantity" data-testid="product-label">
<div class="gl-display-flex gl-flex-direction-row gl-align-items-center"> {{ $options.i18n.ciMinutesLabel }}
</label>
<div class="gl-display-flex gl-flex-direction-row gl-align-items-center gl-mb-6">
<gl-form-input <gl-form-input
ref="quantity" ref="quantity"
v-model.number="quantityModel" v-model.number="quantityModel"
......
...@@ -64,9 +64,8 @@ export default { ...@@ -64,9 +64,8 @@ export default {
}, },
}, },
i18n: { i18n: {
selectedPlanText: s__('Checkout|%{selectedPlanText} plan'),
quantity: s__('Checkout|(x%{quantity})'), quantity: s__('Checkout|(x%{quantity})'),
pricePerUnitPerYear: s__('Checkout|$%{selectedPlanPrice} per pack per year'), pricePerUnitPerYear: s__('Checkout|$%{selectedPlanPrice} per pack of 1,000 minutes'),
dates: s__('Checkout|%{startDate} - %{endDate}'), dates: s__('Checkout|%{startDate} - %{endDate}'),
subtotal: s__('Checkout|Subtotal'), subtotal: s__('Checkout|Subtotal'),
tax: s__('Checkout|Tax'), tax: s__('Checkout|Tax'),
...@@ -81,7 +80,7 @@ export default { ...@@ -81,7 +80,7 @@ export default {
class="gl-display-flex gl-justify-content-space-between gl-font-weight-bold gl-mt-3 gl-mb-3" class="gl-display-flex gl-justify-content-space-between gl-font-weight-bold gl-mt-3 gl-mb-3"
> >
<div data-testid="selected-plan"> <div data-testid="selected-plan">
{{ sprintf($options.i18n.selectedPlanText, { selectedPlanText }) }} {{ selectedPlanText }}
<span v-if="quantity" data-testid="quantity">{{ <span v-if="quantity" data-testid="quantity">{{
sprintf($options.i18n.quantity, { quantity }) sprintf($options.i18n.quantity, { quantity })
}}</span> }}</span>
......
import { GlAlert } from '@gitlab/ui'; import { GlAlert } from '@gitlab/ui';
import { mount, createLocalVue } from '@vue/test-utils'; import { createLocalVue } from '@vue/test-utils';
import { merge } from 'lodash'; import { merge } from 'lodash';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import AddonPurchaseDetails from 'ee/subscriptions/buy_minutes/components/checkout/addon_purchase_details.vue'; import AddonPurchaseDetails from 'ee/subscriptions/buy_minutes/components/checkout/addon_purchase_details.vue';
import subscriptionsResolvers from 'ee/subscriptions/buy_minutes/graphql/resolvers'; import subscriptionsResolvers from 'ee/subscriptions/buy_minutes/graphql/resolvers';
import { STEPS } from 'ee/subscriptions/constants';
import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql'; import stateQuery from 'ee/subscriptions/graphql/queries/state.query.graphql';
import Step from 'ee/vue_shared/purchase_flow/components/step.vue'; import Step from 'ee/vue_shared/purchase_flow/components/step.vue';
import purchaseFlowResolvers from 'ee/vue_shared/purchase_flow/graphql/resolvers'; import purchaseFlowResolvers from 'ee/vue_shared/purchase_flow/graphql/resolvers';
import { stateData as initialStateData } from 'ee_jest/subscriptions/buy_minutes/mock_data'; import { stateData as initialStateData } from 'ee_jest/subscriptions/buy_minutes/mock_data';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(VueApollo); localVue.use(VueApollo);
...@@ -20,21 +21,17 @@ describe('AddonPurchaseDetails', () => { ...@@ -20,21 +21,17 @@ describe('AddonPurchaseDetails', () => {
const createMockApolloProvider = (stateData = {}) => { const createMockApolloProvider = (stateData = {}) => {
const mockApollo = createMockApollo([], resolvers); const mockApollo = createMockApollo([], resolvers);
const data = merge({}, initialStateData, stateData); const data = merge({}, initialStateData, stateData);
mockApollo.clients.defaultClient.cache.writeQuery({ mockApollo.clients.defaultClient.cache.writeQuery({
query: stateQuery, query: stateQuery,
data, data,
}); });
return mockApollo; return mockApollo;
}; };
const createComponent = (stateData = {}) => { const createComponent = (stateData = {}) => {
const apolloProvider = createMockApolloProvider(stateData); const apolloProvider = createMockApolloProvider(stateData);
wrapper = mountExtended(AddonPurchaseDetails, {
return mount(AddonPurchaseDetails, {
localVue, localVue,
apolloProvider, apolloProvider,
stubs: { stubs: {
...@@ -45,11 +42,13 @@ describe('AddonPurchaseDetails', () => { ...@@ -45,11 +42,13 @@ describe('AddonPurchaseDetails', () => {
const findQuantity = () => wrapper.findComponent({ ref: 'quantity' }); const findQuantity = () => wrapper.findComponent({ ref: 'quantity' });
const findGlAlert = () => wrapper.findComponent(GlAlert); const findGlAlert = () => wrapper.findComponent(GlAlert);
const findCiMinutesQuantityText = () => wrapper.find('[data-testid="ci-minutes-quantity-text"]'); const findCiMinutesQuantityText = () => wrapper.findByTestId('ci-minutes-quantity-text');
const findProductLabel = () => wrapper.findByTestId('product-label');
const findSummaryLabel = () => wrapper.findComponent({ ref: 'summary-line-1' });
const isStepValid = () => wrapper.findComponent(Step).props('isValid'); const isStepValid = () => wrapper.findComponent(Step).props('isValid');
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); createComponent();
}); });
afterEach(() => { afterEach(() => {
...@@ -78,12 +77,40 @@ describe('AddonPurchaseDetails', () => { ...@@ -78,12 +77,40 @@ describe('AddonPurchaseDetails', () => {
}); });
it('is invalid when quantity is less than 1', async () => { it('is invalid when quantity is less than 1', async () => {
wrapper = createComponent({ createComponent({
subscription: { namespaceId: 483, quantity: 0 }, subscription: { namespaceId: 483, quantity: 0 },
}); });
await nextTick();
expect(isStepValid()).toBe(false); expect(isStepValid()).toBe(false);
}); });
describe('labels', () => {
describe('when quantity is 1', () => {
it('shows the correct product label', () => {
expect(findProductLabel().text()).toBe('CI minute pack');
});
it('shows the correct summary label', () => {
createComponent({ activeStep: STEPS[1] });
expect(findSummaryLabel().text()).toBe('1 CI minute pack');
});
});
describe('when quantity is more than 1', () => {
const stateData = { subscription: { namespaceId: 483, quantity: 2 } };
it('shows the correct product label', () => {
createComponent(stateData);
expect(findProductLabel().text()).toBe('CI minute pack');
});
it('shows the correct summary label', () => {
createComponent({ ...stateData, activeStep: STEPS[1] });
expect(findSummaryLabel().text()).toBe('2 CI minute packs');
});
});
});
}); });
...@@ -40,13 +40,11 @@ describe('SummaryDetails', () => { ...@@ -40,13 +40,11 @@ describe('SummaryDetails', () => {
}); });
it('renders the plan name', () => { it('renders the plan name', () => {
expect(wrapper.findByTestId('selected-plan').text()).toMatchInterpolatedText( expect(wrapper.findByTestId('selected-plan').text()).toMatchInterpolatedText('Test (x1)');
'Test plan (x1)',
);
}); });
it('renders the price per unit', () => { it('renders the price per unit', () => {
expect(wrapper.findByTestId('price-per-unit').text()).toBe('$10 per pack per year'); expect(wrapper.findByTestId('price-per-unit').text()).toBe('$10 per pack of 1,000 minutes');
}); });
it('displays the total amount', () => { it('displays the total amount', () => {
......
...@@ -5,7 +5,7 @@ export const mockCiMinutesPlans = [ ...@@ -5,7 +5,7 @@ export const mockCiMinutesPlans = [
id: 'ciMinutesPackPlanId', id: 'ciMinutesPackPlanId',
code: 'ci_minutes', code: 'ci_minutes',
pricePerYear: 10, pricePerYear: 10,
name: '1000 CI minutes pack', name: 'CI minutes pack',
__typename: 'Plan', __typename: 'Plan',
}, },
]; ];
......
...@@ -6526,12 +6526,17 @@ msgstr "" ...@@ -6526,12 +6526,17 @@ msgstr ""
msgid "Checkout" msgid "Checkout"
msgstr "" msgstr ""
msgid "Checkout|$%{selectedPlanPrice} per pack per year" msgid "Checkout|$%{selectedPlanPrice} per pack of 1,000 minutes"
msgstr "" msgstr ""
msgid "Checkout|$%{selectedPlanPrice} per user per year" msgid "Checkout|$%{selectedPlanPrice} per user per year"
msgstr "" msgstr ""
msgid "Checkout|%d CI minute pack"
msgid_plural "Checkout|%d CI minute packs"
msgstr[0] ""
msgstr[1] ""
msgid "Checkout|%{cardType} ending in %{lastFourDigits}" msgid "Checkout|%{cardType} ending in %{lastFourDigits}"
msgstr "" msgstr ""
...@@ -6541,15 +6546,17 @@ msgstr "" ...@@ -6541,15 +6546,17 @@ msgstr ""
msgid "Checkout|%{name}'s GitLab subscription" msgid "Checkout|%{name}'s GitLab subscription"
msgstr "" msgstr ""
msgid "Checkout|%{quantity} CI minute packs"
msgstr ""
msgid "Checkout|%{selectedPlanText} plan" msgid "Checkout|%{selectedPlanText} plan"
msgstr "" msgstr ""
msgid "Checkout|%{startDate} - %{endDate}" msgid "Checkout|%{startDate} - %{endDate}"
msgstr "" msgstr ""
msgid "Checkout|%{totalCiMinutes} CI minute"
msgid_plural "Checkout|%{totalCiMinutes} CI minutes"
msgstr[0] ""
msgstr[1] ""
msgid "Checkout|%{totalCiMinutes} CI minutes" msgid "Checkout|%{totalCiMinutes} CI minutes"
msgstr "" msgstr ""
...@@ -6565,7 +6572,7 @@ msgstr "" ...@@ -6565,7 +6572,7 @@ msgstr ""
msgid "Checkout|Billing address" msgid "Checkout|Billing address"
msgstr "" msgstr ""
msgid "Checkout|CI minute packs" msgid "Checkout|CI minute pack"
msgstr "" msgstr ""
msgid "Checkout|CI minute packs are only used after you've used your subscription's monthly quota. The additional minutes will roll over month to month and are valid for one year." msgid "Checkout|CI minute packs are only used after you've used your subscription's monthly quota. The additional minutes will roll over month to month and are valid for one year."
......
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