Commit 42ef6cd9 authored by Simon Knox's avatar Simon Knox

Merge branch '341171-mlunoe-subscription-history-table-add-future-subscriptions' into 'master'

Feat(SM: Subscription History): add future subscr

See merge request gitlab-org/gitlab!79015
parents 31987b3f ad7f40bc
......@@ -11,13 +11,15 @@ import {
subscriptionHistoryFailedTitle,
subscriptionHistoryFailedMessage,
currentSubscriptionsEntryName,
historySubscriptionsEntryName,
pastSubscriptionsEntryName,
futureSubscriptionsEntryName,
subscriptionMainTitle,
exportLicenseUsageBtnText,
SUBSCRIPTION_ACTIVATION_SUCCESS_EVENT,
} from '../constants';
import getCurrentLicense from '../graphql/queries/get_current_license.query.graphql';
import getLicenseHistory from '../graphql/queries/get_license_history.query.graphql';
import getPastLicenseHistory from '../graphql/queries/get_past_license_history.query.graphql';
import getFutureLicenseHistory from '../graphql/queries/get_future_license_history.query.graphql';
import SubscriptionActivationCard from './subscription_activation_card.vue';
import SubscriptionBreakdown from './subscription_breakdown.vue';
import SubscriptionPurchaseCard from './subscription_purchase_card.vue';
......@@ -63,21 +65,31 @@ export default {
this.subscriptionFetchError = currentSubscriptionsEntryName;
},
},
subscriptionHistory: {
query: getLicenseHistory,
pastLicenseHistoryEntries: {
query: getPastLicenseHistory,
update({ licenseHistoryEntries }) {
return licenseHistoryEntries?.nodes || [];
},
error() {
this.subscriptionFetchError = historySubscriptionsEntryName;
this.subscriptionFetchError = pastSubscriptionsEntryName;
},
},
futureLicenseHistoryEntries: {
query: getFutureLicenseHistory,
update({ subscriptionFutureEntries }) {
return subscriptionFutureEntries?.nodes || [];
},
error() {
this.subscriptionFetchError = futureSubscriptionsEntryName;
},
},
},
data() {
return {
currentSubscription: {},
pastLicenseHistoryEntries: [],
futureLicenseHistoryEntries: [],
activationNotification: null,
subscriptionHistory: [],
subscriptionFetchError: null,
};
},
......@@ -88,6 +100,9 @@ export default {
canShowSubscriptionDetails() {
return this.hasActiveLicense || this.hasValidSubscriptionData;
},
subscriptionHistory() {
return [...this.futureLicenseHistoryEntries, ...this.pastLicenseHistoryEntries];
},
},
created() {
this.$options.activationListeners = {
......
......@@ -64,6 +64,13 @@ export default {
},
{
key: 'activatedAt',
formatter: (v, k, { activatedAt }) => {
if (!activatedAt) {
return '-';
}
return activatedAt;
},
label: subscriptionTable.activatedAt,
tdAttr,
tdClass: this.cellClass,
......@@ -109,11 +116,9 @@ export default {
isCurrentSubscription({ id }) {
return id === this.currentSubscriptionId;
},
rowAttr(item) {
rowAttr() {
return {
'data-testid': this.isCurrentSubscription(item)
? 'subscription-current'
: 'subscription-history-row',
'data-testid': 'subscription-history-row',
};
},
rowClass(item) {
......
......@@ -22,7 +22,8 @@ export const subscriptionHistoryFailedMessage = s__(
'SuperSonics|Your %{subscriptionEntryName} cannot be displayed at the moment. Please refresh the page to try again.',
);
export const currentSubscriptionsEntryName = s__('SuperSonics|current subscription');
export const historySubscriptionsEntryName = s__('SuperSonics|history subscriptions');
export const pastSubscriptionsEntryName = s__('SuperSonics|past subscriptions');
export const futureSubscriptionsEntryName = s__('SuperSonics|future subscriptions');
export const cancelLabel = __('Cancel');
export const activateLabel = s__('AdminUsers|Activate');
......
query getFutureLicenseHistory {
subscriptionFutureEntries {
nodes {
type
plan
name
email
company
usersInLicenseCount
startsAt
expiresAt
}
}
}
import produce from 'immer';
import getCurrentLicense from './queries/get_current_license.query.graphql';
import getLicenseHistory from './queries/get_license_history.query.graphql';
import getPastLicenseHistory from './queries/get_past_license_history.query.graphql';
export const getLicenseFromData = ({ data } = {}) => data?.gitlabSubscriptionActivate?.license;
export const getErrorsAsData = ({ data } = {}) => data?.gitlabSubscriptionActivate?.errors || [];
......@@ -14,12 +14,12 @@ export const updateSubscriptionAppCache = (cache, mutation) => {
draftData.currentLicense = license;
});
cache.writeQuery({ query: getCurrentLicense, data });
const subscriptionsList = cache.readQuery({ query: getLicenseHistory });
const subscriptionListData = produce(subscriptionsList, (draftData) => {
const pastSubscriptions = cache.readQuery({ query: getPastLicenseHistory });
const pastSubscriptionsData = produce(pastSubscriptions, (draftData) => {
draftData.licenseHistoryEntries.nodes = [
license,
...subscriptionsList.licenseHistoryEntries.nodes,
...pastSubscriptions.licenseHistoryEntries.nodes,
];
});
cache.writeQuery({ query: getLicenseHistory, data: subscriptionListData });
cache.writeQuery({ query: getPastLicenseHistory, data: pastSubscriptionsData });
};
......@@ -27,7 +27,7 @@ import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisse
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import axios from '~/lib/utils/axios_utils';
import { license, subscriptionHistory } from '../mock_data';
import { license, subscriptionPastHistory, subscriptionFutureHistory } from '../mock_data';
describe('Subscription Breakdown', () => {
let axiosMock;
......@@ -35,7 +35,7 @@ describe('Subscription Breakdown', () => {
let glModalDirective;
let userCalloutDismissSpy;
const [, licenseFile] = subscriptionHistory;
const [, licenseFile] = subscriptionPastHistory;
const congratulationSvgPath = '/path/to/svg';
const connectivityHelpURL = 'connectivity/help/url';
const customersPortalUrl = 'customers.dot';
......@@ -88,7 +88,7 @@ describe('Subscription Breakdown', () => {
},
propsData: {
subscription: license.ULTIMATE,
subscriptionList: subscriptionHistory,
subscriptionList: [...subscriptionFutureHistory, ...subscriptionPastHistory],
...props,
},
stubs: {
......@@ -398,7 +398,10 @@ describe('Subscription Breakdown', () => {
it('provides the correct props to the subscription history component', () => {
expect(findDetailsHistory().props('currentSubscriptionId')).toBe(license.ULTIMATE.id);
expect(findDetailsHistory().props('subscriptionList')).toBe(subscriptionHistory);
expect(findDetailsHistory().props('subscriptionList')).toMatchObject([
...subscriptionFutureHistory,
...subscriptionPastHistory,
]);
});
});
......
import { GlBadge, GlIcon, GlTooltip } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import SubscriptionDetailsHistory from 'ee/admin/subscriptions/show/components/subscription_details_history.vue';
import { detailsLabels, cloudLicenseText } from 'ee/admin/subscriptions/show/constants';
import {
detailsLabels,
cloudLicenseText,
licenseFileText,
subscriptionTypes,
} from 'ee/admin/subscriptions/show/constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { license, subscriptionHistory } from '../mock_data';
import { license, subscriptionFutureHistory, subscriptionPastHistory } from '../mock_data';
const subscriptionList = [...subscriptionFutureHistory, ...subscriptionPastHistory];
const currentSubscriptionIndex = subscriptionFutureHistory.length;
describe('Subscription Details History', () => {
let wrapper;
const findCurrentRow = () => wrapper.findByTestId('subscription-current');
const findTableRows = () => wrapper.findAllByTestId('subscription-history-row');
const findCurrentRow = () => findTableRows().at(currentSubscriptionIndex);
const cellFinder = (row) => (testId) => extendedWrapper(row).findByTestId(testId);
const containsABadge = (row) => row.findComponent(GlBadge).exists();
const containsATooltip = (row) => row.findComponent(GlTooltip).exists();
......@@ -20,7 +29,7 @@ describe('Subscription Details History', () => {
mount(SubscriptionDetailsHistory, {
propsData: {
currentSubscriptionId: license.ULTIMATE.id,
subscriptionList: subscriptionHistory,
subscriptionList,
...props,
},
}),
......@@ -41,12 +50,12 @@ describe('Subscription Details History', () => {
});
it('has the correct number of subscription rows', () => {
expect(findTableRows()).toHaveLength(1);
expect(findTableRows()).toHaveLength(subscriptionList.length);
});
it('has the correct license type', () => {
expect(findCurrentRow().text()).toContain(cloudLicenseText);
expect(findTableRows().at(0).text()).toContain('License file');
expect(findTableRows().at(-1).text()).toContain('License file');
});
it('has a badge for the license type', () => {
......@@ -73,49 +82,53 @@ describe('Subscription Details History', () => {
expect(findTableRows().at(0).classes('gl-text-blue-500')).toBe(false);
});
describe('cell data', () => {
describe.each(Object.entries(subscriptionList))('cell data index=%#', (index, subscription) => {
let findCellByTestid;
beforeEach(() => {
createComponent();
findCellByTestid = cellFinder(findCurrentRow());
findCellByTestid = cellFinder(findTableRows().at(index));
});
it.each`
testId | key
${'starts-at'} | ${'startsAt'}
${'starts-at'} | ${'startsAt'}
${'expires-at'} | ${'expiresAt'}
${'users-in-license-count'} | ${'usersInLicenseCount'}
`('displays the correct value for the $testId cell', ({ testId, key }) => {
const cellTestId = `subscription-cell-${testId}`;
expect(findCellByTestid(cellTestId).text()).toBe(subscriptionHistory[0][key]);
const value = subscription[key] || '-';
expect(findCellByTestid(cellTestId).text()).toBe(value);
});
it('displays the name field with tooltip', () => {
const cellTestId = 'subscription-cell-name';
const text = findCellByTestid(cellTestId).text();
expect(text).toContain(subscriptionHistory[0].name);
expect(text).toContain(`(${subscriptionHistory[0].company})`);
expect(text).toContain(subscriptionHistory[0].email);
expect(text).toContain(subscription.name);
expect(text).toContain(`(${subscription.company})`);
expect(text).toContain(subscription.email);
});
it('displays sr-only element for screen readers', () => {
const testId = 'subscription-history-sr-only';
const text = findCellByTestid(testId).text();
expect(text).not.toContain(subscriptionHistory[0].name);
expect(text).toContain(`(${detailsLabels.company}: ${subscriptionHistory[0].company})`);
expect(text).toContain(`${detailsLabels.email}: ${subscriptionHistory[0].email}`);
expect(text).not.toContain(subscription.name);
expect(text).toContain(`(${detailsLabels.company}: ${subscription.company})`);
expect(text).toContain(`${detailsLabels.email}: ${subscription.email}`);
});
it('displays the correct value for the type cell', () => {
const cellTestId = `subscription-cell-type`;
expect(findCellByTestid(cellTestId).text()).toBe(cloudLicenseText);
const type =
subscription.type === subscriptionTypes.LICENSE_FILE ? licenseFileText : cloudLicenseText;
expect(findCellByTestid(cellTestId).text()).toBe(type);
});
it('displays the correct value for the plan cell', () => {
const cellTestId = `subscription-cell-plan`;
expect(findCellByTestid(cellTestId).text()).toBe('Ultimate');
expect(findCellByTestid(cellTestId).text()).toBe(
capitalizeFirstCharacter(subscription.plan),
);
});
});
});
......
......@@ -35,7 +35,7 @@ export const license = {
},
};
export const subscriptionHistory = [
export const subscriptionPastHistory = [
{
activatedAt: '2022-03-16',
company: 'ACME Corp',
......@@ -62,6 +62,29 @@ export const subscriptionHistory = [
},
];
export const subscriptionFutureHistory = [
{
company: 'ACME Corp',
email: 'user@acmecorp.com',
expiresAt: '2023-03-16',
name: 'Jane Doe',
plan: 'ultimate',
startsAt: '2022-03-11',
type: subscriptionTypes.CLOUD,
usersInLicenseCount: '15',
},
{
company: 'ACME Corp',
email: 'user@acmecorp.com',
expiresAt: '2022-03-16',
name: 'Jane Doe',
plan: 'ultimate',
startsAt: '2021-03-16',
type: subscriptionTypes.CLOUD,
usersInLicenseCount: '10',
},
];
export const activateLicenseMutationResponse = {
FAILURE: [
{
......
......@@ -34942,7 +34942,10 @@ msgstr ""
msgid "SuperSonics|current subscription"
msgstr ""
msgid "SuperSonics|history subscriptions"
msgid "SuperSonics|future subscriptions"
msgstr ""
msgid "SuperSonics|past subscriptions"
msgstr ""
msgid "Support"
......
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