add more e2e tests.
Этот коммит содержится в:
@@ -88,18 +88,18 @@
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.totalCost {
|
||||
.totalCostWarning {
|
||||
width: 141px;
|
||||
}
|
||||
|
||||
.totalCost > span:first-child {
|
||||
.totalCostWarning > span:first-child {
|
||||
color: var(--sys-denim-center-channel-text);
|
||||
font-family: 'Open Sans';
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.totalCost > span:last-child {
|
||||
.totalCostWarning > span:last-child {
|
||||
color: rgba(var(--sys-denim-center-channel-text-rgb), 0.72);
|
||||
font-family: 'Open Sans';
|
||||
font-size: 12px;
|
||||
|
||||
@@ -18,7 +18,6 @@ import {findSelfHostedProductBySku} from 'utils/hosted_customer';
|
||||
import ExternalLink from 'components/external_link';
|
||||
|
||||
const MONTHS_IN_YEAR = 12;
|
||||
const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
|
||||
const MAX_TRANSACTION_VALUE = 1_000_000 - 1;
|
||||
|
||||
interface Props {
|
||||
@@ -35,14 +34,14 @@ export default function SelfHostedExpansionCard(props: Props) {
|
||||
const endsAt = moment(parseInt(license.ExpiresAt, 10)).format('MMM. D, YYYY');
|
||||
const [additionalSeats, setAdditionalSeats] = useState(props.initialSeats);
|
||||
const [overMaxSeats, setOverMaxSeats] = useState(false);
|
||||
const licenseExpiry = parseInt(license.ExpiresAt, 10);
|
||||
const licenseExpiry = new Date(parseInt(license.ExpiresAt, 10));
|
||||
const invalidAdditionalSeats = additionalSeats === 0 || isNaN(additionalSeats);
|
||||
const [products] = useGetSelfHostedProducts();
|
||||
const currentProduct = findSelfHostedProductBySku(products, license.SkuShortName);
|
||||
|
||||
const getMonthsUntilExpiry = () => {
|
||||
const now = new Date();
|
||||
return Math.ceil((licenseExpiry - now.getTime()) / MILLISECONDS_PER_DAY / 30);
|
||||
return (licenseExpiry.getMonth() - now.getMonth()) + 12 * (licenseExpiry.getFullYear() - now.getFullYear());
|
||||
};
|
||||
|
||||
const getMonthlyPrice = () => {
|
||||
@@ -209,7 +208,7 @@ export default function SelfHostedExpansionCard(props: Props) {
|
||||
<br/>
|
||||
<FormattedMessage
|
||||
id='self_hosted_expansion_rhs_card_cost_per_user_breakdown'
|
||||
defaultMessage='{costPerUser} x {monthsUntilExpiry} months'
|
||||
defaultMessage='${costPerUser} x {monthsUntilExpiry} months'
|
||||
values={{
|
||||
costPerUser: getMonthlyPrice().toFixed(2),
|
||||
monthsUntilExpiry: getMonthsUntilExpiry(),
|
||||
@@ -219,7 +218,7 @@ export default function SelfHostedExpansionCard(props: Props) {
|
||||
<div className='costAmount'>
|
||||
<span>{'$' + getCostPerUser().toFixed(2)}</span>
|
||||
</div>
|
||||
<div className='totalCost'>
|
||||
<div className='totalCostWarning'>
|
||||
<FormattedMessage
|
||||
id='self_hosted_expansion_rhs_card_total_title'
|
||||
defaultMessage='Total'
|
||||
@@ -230,7 +229,7 @@ export default function SelfHostedExpansionCard(props: Props) {
|
||||
defaultMessage='The total will be prorated'
|
||||
/>
|
||||
</div>
|
||||
<span className='costAmount'>
|
||||
<span className='totalCostAmount'>
|
||||
<span>{'$' + getTotal().toFixed(2)}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import {screen, fireEvent} from '@testing-library/react';
|
||||
import {screen, fireEvent, waitFor} from '@testing-library/react';
|
||||
|
||||
import {GlobalState} from 'types/store';
|
||||
|
||||
@@ -11,7 +11,7 @@ import {SelfHostedSignupForm, SelfHostedSignupProgress} from '@mattermost/types/
|
||||
|
||||
import {renderWithIntlAndStore} from 'tests/react_testing_utils';
|
||||
import {TestHelper as TH} from 'utils/test_helper';
|
||||
import {SelfHostedProducts, ModalIdentifiers} from 'utils/constants';
|
||||
import {SelfHostedProducts, ModalIdentifiers, RecurringIntervals} from 'utils/constants';
|
||||
|
||||
import {DeepPartial} from '@mattermost/types/utilities';
|
||||
|
||||
@@ -76,6 +76,7 @@ const mockProfessionalProduct = TH.getProductMock({
|
||||
name: 'Professional',
|
||||
sku: SelfHostedProducts.PROFESSIONAL,
|
||||
price_per_seat: 7.5,
|
||||
recurring_interval: RecurringIntervals.MONTH
|
||||
});
|
||||
|
||||
jest.mock('mattermost-redux/client', () => {
|
||||
@@ -129,6 +130,11 @@ jest.mock('utils/hosted_customer', () => {
|
||||
|
||||
const productName = SelfHostedProducts.PROFESSIONAL;
|
||||
|
||||
// Licensed expiry set as 3 months from the current date (rolls over to new years).
|
||||
const licenseExpiry = new Date();
|
||||
const monthsUntilLicenseExpiry = 3;
|
||||
licenseExpiry.setMonth(licenseExpiry.getMonth() + monthsUntilLicenseExpiry);
|
||||
|
||||
const initialState: DeepPartial<GlobalState> = {
|
||||
views: {
|
||||
modals: {
|
||||
@@ -143,11 +149,6 @@ const initialState: DeepPartial<GlobalState> = {
|
||||
storage: {},
|
||||
},
|
||||
entities: {
|
||||
admin: {
|
||||
analytics: {
|
||||
TOTAL_USERS: existingUsers,
|
||||
},
|
||||
},
|
||||
teams: {
|
||||
currentTeamId: '',
|
||||
},
|
||||
@@ -163,6 +164,7 @@ const initialState: DeepPartial<GlobalState> = {
|
||||
license: {
|
||||
Sku: productName,
|
||||
Users: '50',
|
||||
ExpiresAt: licenseExpiry.getTime().toString()
|
||||
},
|
||||
},
|
||||
cloud: {
|
||||
@@ -224,6 +226,7 @@ interface PurchaseForm {
|
||||
state: string;
|
||||
zip: string;
|
||||
seats: string;
|
||||
agree: boolean;
|
||||
}
|
||||
|
||||
const defaultSuccessForm: PurchaseForm = {
|
||||
@@ -236,6 +239,7 @@ const defaultSuccessForm: PurchaseForm = {
|
||||
state: 'MN',
|
||||
zip: '55423',
|
||||
seats: '10',
|
||||
agree: true,
|
||||
};
|
||||
|
||||
function fillForm(form: PurchaseForm) {
|
||||
@@ -248,6 +252,9 @@ function fillForm(form: PurchaseForm) {
|
||||
selectDropdownValue('selfHostedExpansionStateSelector', form.state);
|
||||
changeByPlaceholder('Zip/Postal Code', form.zip);
|
||||
changeByTestId('seatsInput', form.seats);
|
||||
if (form.agree) {
|
||||
fireEvent.click(screen.getByText('I have read and agree', {exact: false}));
|
||||
}
|
||||
|
||||
expect(document.getElementsByClassName('SelfHostedExpansionRHSCard__AddSeatsWarning')[0] as HTMLElement).toBeEnabled();
|
||||
|
||||
@@ -263,7 +270,7 @@ function fillForm(form: PurchaseForm) {
|
||||
return completeButton;
|
||||
}
|
||||
|
||||
describe('SelfHostedExpansionModal', () => {
|
||||
describe('SelfHostedExpansionModal Open', () => {
|
||||
it('renders the form', () => {
|
||||
renderWithIntlAndStore(<div id='root-portal'><SelfHostedExpansionModal/></div>, initialState);
|
||||
|
||||
@@ -321,7 +328,45 @@ describe('SelfHostedExpansionModal', () => {
|
||||
// });
|
||||
});
|
||||
|
||||
describe('SelfHostedExpansionModal :: canSubmit', () => {
|
||||
describe('SelfHostedExpansionModal RHS Card', () => {
|
||||
it("New seats input should be pre-populated with the difference from the active users and licensed seats", () => {
|
||||
renderWithIntlAndStore(<div id='root-portal'><SelfHostedExpansionModal/></div>, initialState);
|
||||
|
||||
const expectedPrePopulatedSeats = (initialState.entities?.users?.filteredStats?.total_users_count || 1) - parseInt(initialState.entities?.general?.license?.Users || '0', 10);
|
||||
|
||||
const seatsField = screen.getByTestId('seatsInput').querySelector('input');
|
||||
expect(seatsField).toBeInTheDocument();
|
||||
expect(seatsField?.value).toBe(expectedPrePopulatedSeats.toString());
|
||||
});
|
||||
|
||||
it("Cost per User should be represented as the current subscription price multiplied by the remaining months", () => {
|
||||
renderWithIntlAndStore(<div id='root-portal'><SelfHostedExpansionModal/></div>, initialState);
|
||||
|
||||
const expectedCostPerUser = monthsUntilLicenseExpiry * mockProfessionalProduct.price_per_seat;
|
||||
|
||||
const costPerUser = document.getElementsByClassName('costPerUser')[0];
|
||||
expect(costPerUser).toBeInTheDocument();
|
||||
expect(costPerUser.innerHTML).toContain('Cost per user<br>$' + mockProfessionalProduct.price_per_seat.toFixed(2) + ' x ' + monthsUntilLicenseExpiry + ' months');
|
||||
|
||||
const costAmount = document.getElementsByClassName('costAmount')[0];
|
||||
expect(costAmount).toBeInTheDocument();
|
||||
expect(costAmount.innerHTML).toContain('$' + expectedCostPerUser)
|
||||
});
|
||||
|
||||
it("Total cost User should be represented as the current subscription price multiplied by the remaining months multiplied by the number of users", () => {
|
||||
renderWithIntlAndStore(<div id='root-portal'><SelfHostedExpansionModal/></div>, initialState);
|
||||
const seatsInputValue = 100;
|
||||
changeByTestId('seatsInput', seatsInputValue.toString());
|
||||
|
||||
const expectedTotalCost = monthsUntilLicenseExpiry * mockProfessionalProduct.price_per_seat * seatsInputValue;
|
||||
|
||||
const costAmount = document.getElementsByClassName('totalCostAmount')[0];
|
||||
expect(costAmount).toBeInTheDocument();
|
||||
expect(costAmount.innerHTML).toContain('$' + expectedTotalCost)
|
||||
});
|
||||
});
|
||||
|
||||
describe('SelfHostedExpansionModal Submit', () => {
|
||||
function makeHappyPathState(): FormState {
|
||||
return {
|
||||
address: 'string',
|
||||
@@ -366,7 +411,6 @@ describe('SelfHostedExpansionModal :: canSubmit', () => {
|
||||
expect(canSubmit(state, SelfHostedSignupProgress.PAID)).toBe(true);
|
||||
});
|
||||
|
||||
// TODO: Needed?
|
||||
it('if created subscription, can submit', () => {
|
||||
const state = makeInitialState(1);
|
||||
state.submitting = false;
|
||||
|
||||
@@ -409,6 +409,7 @@ export default function SelfHostedExpansionModal() {
|
||||
/>
|
||||
</span>
|
||||
<Address
|
||||
testPrefix='selfHostedExpansion'
|
||||
type='billing'
|
||||
country={formState.country}
|
||||
changeCountry={(option) => {
|
||||
@@ -450,6 +451,7 @@ export default function SelfHostedExpansionModal() {
|
||||
/>
|
||||
</div>
|
||||
<Address
|
||||
testPrefix='selfHostedExpansion'
|
||||
type='shipping'
|
||||
country={formState.shippingCountry}
|
||||
changeCountry={(option) => {
|
||||
|
||||
Ссылка в новой задаче
Block a user