Skip to content

Commit

Permalink
feat: Normalize tests to passing state (#338)
Browse files Browse the repository at this point in the history
* feat: Normalize tests

* feat: adds policy title autogeneration and subsidy title validation

* feat: additional requested changes added

* feat: More test written for coverage

* chore: Stray comments, line breaks and unused dependencies removed

* feat: more tests

* chore: final test
  • Loading branch information
brobro10000 authored Jun 2, 2023
1 parent fc954ad commit 45530bb
Show file tree
Hide file tree
Showing 20 changed files with 359 additions and 58 deletions.
1 change: 1 addition & 0 deletions src/Configuration/Provisioning/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const Dashboard = () => {
}
hydrateEnterpriseSubsidies(25, editAction, editLearnerCreditPlan);
}, [toastText.successfulPlanCreation, history, location, locationState]);

return (
<>
<DashboardHeader />
Expand Down
5 changes: 2 additions & 3 deletions src/Configuration/Provisioning/DashboardDatatable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { DataTable, TextFilter } from '@edx/paragon';
import React, { useEffect, useState } from 'react';
import { useContextSelector } from 'use-context-selector';
import { DashboardContext } from './DashboardContext';
import { MAX_PAGE_SIZE } from './data/constants';

const DashboardDatatable = () => {
const data = useContextSelector(DashboardContext, v => v[0]);
Expand All @@ -15,7 +16,6 @@ const DashboardDatatable = () => {

// Implementation due to filterText value displaying accessor value customerName as opposed to Customer Name
const filterStatus = (rest) => <DataTable.FilterStatus showFilteredFields={false} {...rest} />;

return (
<section className="mt-5">
<DataTable
Expand All @@ -24,11 +24,10 @@ const DashboardDatatable = () => {
isFilterable
defaultColumnValues={{ Filter: TextFilter }}
initialState={{
pageSize: 12,
pageSize: MAX_PAGE_SIZE,
pageIndex: 0,
}}
itemCount={learnerCreditCustomers?.length}
pageCount={12}
data={learnerCreditCustomers}
FilterStatusComponent={filterStatus}
columns={[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ErrorPageButton = ({
);

ErrorPageButton.propTypes = {
as: PropTypes.node.isRequired,
as: PropTypes.oneOfType([PropTypes.node, PropTypes.shape({})]).isRequired,
to: PropTypes.string.isRequired,
className: PropTypes.string,
children: PropTypes.node.isRequired,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import React, { useCallback, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { Form } from '@edx/paragon';
import PROVISIONING_PAGE_TEXT from '../../data/constants';
import useProvisioningContext from '../../data/hooks';
import { extractDefinedCatalogTitle, indexOnlyPropType, selectProvisioningContext } from '../../data/utils';
import {
extractDefinedCatalogTitle, generatePolicyName, indexOnlyPropType, selectProvisioningContext,
} from '../../data/utils';
import { isWholeDollarAmount } from '../../../../utils';

const ProvisioningFormAccountDetails = ({ index }) => {
const { ACCOUNT_DETAIL } = PROVISIONING_PAGE_TEXT.FORM;
const { setAccountName, setAccountValue, setInvalidPolicyFields } = useProvisioningContext();
const [multipleFunds, formData, showInvalidField] = selectProvisioningContext('multipleFunds', 'formData', 'showInvalidField');

const { policies } = showInvalidField;
const isAccountNameDefinedAndFalse = policies[index]?.accountName === false;
const isAccountValueDefinedAndFalse = policies[index]?.accountValue === false;

const formFeedbackText = multipleFunds
? ACCOUNT_DETAIL.OPTIONS.totalAccountValue.dynamicSubtitle(extractDefinedCatalogTitle(formData.policies[index]))
: ACCOUNT_DETAIL.OPTIONS.totalAccountValue.subtitle;

const [accountValueState, setAccountValueState] = useState('');
const [accountNameState, setAccountNameState] = useState('');
const [isWholeDollar, setIsWholeDollar] = useState(true);
Expand All @@ -40,6 +45,13 @@ const ProvisioningFormAccountDetails = ({ index }) => {
}
}, [index, formData]);

useEffect(() => {
if (formData.subsidyTitle) {
setAccountName({ accountName: generatePolicyName(formData, index) }, index);
setAccountNameState(generatePolicyName(formData, index));
}
}, [formData.subsidyTitle, formData.policies[index]?.catalogQueryMetadata, index]);

return (
<article className="mt-4.5">
<div className="mb-1">
Expand All @@ -48,12 +60,14 @@ const ProvisioningFormAccountDetails = ({ index }) => {
<Form.Group
className="mt-3.5 mb-1"
isInvalid={isAccountNameDefinedAndFalse}
disabled
>
<Form.Control
floatingLabel={ACCOUNT_DETAIL.OPTIONS.displayName}
value={accountNameState}
onChange={handleChange}
data-testid="account-name"
disabled
/>
{isAccountNameDefinedAndFalse && (
<Form.Control.Feedback
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,20 +53,21 @@ const ProvisioningFormCatalog = ({ index }) => {
<div>
<h3>{CATALOG.TITLE}</h3>
</div>
{multipleFunds && (
<Form.Group className="mt-3.5">
<Form.Label className="mb-2.5">{CATALOG.SUB_TITLE}</Form.Label>
{multipleFunds && (
<h4>
{extractDefinedCatalogTitle(formData.policies[index])}
</h4>
)}
{multipleFunds === false && (
<Form.Group className="mt-3.5">
<Form.Label className="mb-2.5">{CATALOG.SUB_TITLE}</Form.Label>
<Form.RadioSet
name="display-catalog-content"
onChange={handleChange}
value={value || formData.policies[index].catalogCategory}
>
{
)}
{multipleFunds === false && (
<>
<Form.RadioSet
name="display-catalog-content"
onChange={handleChange}
value={value || formData.policies[index].catalogCategory}
>
{
Object.keys(CATALOG.OPTIONS).map((key) => (
<Form.Radio
value={CATALOG.OPTIONS[key]}
Expand All @@ -80,16 +81,17 @@ const ProvisioningFormCatalog = ({ index }) => {
</Form.Radio>
))
}
</Form.RadioSet>
{!customCatalogSelected && isCatalogQueryMetadataDefinedAndFalse && (
</Form.RadioSet>
{!customCatalogSelected && isCatalogQueryMetadataDefinedAndFalse && (
<Form.Control.Feedback
type="invalid"
>
{CATALOG.ERROR}
</Form.Control.Feedback>
)}
</Form.Group>
)}
)}
</>
)}
</Form.Group>
</article>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,26 @@ describe('ProvisioningFormAccountDetails', () => {
fireEvent.change(input, { target: { value: '100.50' } });
expect(screen.getByText(ALERTS.incorrectDollarAmount)).toBeTruthy();
});
it('autogenerates name from subsidyTitle', () => {
const updatedInitialState = {
...initialStateValue,
multipleFunds: false,
formData: {
...initialStateValue.formData,
subsidyTitle: 'Test Subsidy Title',
policies: INITIAL_CATALOG_QUERIES.defaultQuery,
},
};
renderWithRouter(
<ProvisioningFormAccountDetailsWrapper
value={updatedInitialState}
index={0}
/>,
);

expect(screen.getByText(ACCOUNT_DETAIL.OPTIONS.displayName)).toBeTruthy();
const input = screen.getByTestId('account-name');

expect(input.getAttribute('value')).toEqual('Test Subsidy Title --- ');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ const ProvisioningFormAccountType = () => {

const handleChange = async (e) => {
const newTabValue = e.target.value;
if (!formData?.subsidyTitle) {
setInvalidSubsidyFields({ ...subsidy, subsidyTitle: false });
global.scrollTo({ top: 0, behavior: 'smooth' });
return;
}
if (catalogQueries.data.length === 0) {
try {
handleSpinnerLoadingState(newTabValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ const ProvisioningFormCustomer = () => {
const { value, dataset } = newEvent;
if (dataset.testid === 'customer-financial-identifier') {
if (value !== '' && !isValidOpportunityProduct(value)) {
setIsOpportunityProduct(false);
if (value.length !== 19) {
setIsOpportunityProduct(false);
return;
}
return;
}
setIsOpportunityProduct(true);
Expand All @@ -42,6 +45,7 @@ const ProvisioningFormCustomer = () => {
<Form.Group
className="mt-3.5"
isInvalid={(!isOpportunityProduct || isOpportunityProductDefinedAndFalse)}
spellCheck="false"
>
<Form.Control
floatingLabel={CUSTOMER.FINANCIAL_IDENTIFIER.TITLE}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ const ProvisioningFormSubmissionButton = () => {
const handleSubmit = async () => {
setSubmitButtonState('pending');
setAlertMessage(false);

// Checks validiy before performing any API calls
if (policies.length === 0 || !canCreatePolicyAndSubsidy) {
setSubmitButtonState('error');
Expand Down Expand Up @@ -210,14 +209,15 @@ const ProvisioningFormSubmissionButton = () => {
<ActionRow className="justify-content-start mt-5">
<StatefulButton
labels={buttonLabels}
variant="primary"
variant={submitButtonState === 'error' ? 'danger' : 'primary'}
state={submitButtonState}
onClick={handleSubmit}
/>
<Button
variant="secondary"
value={BUTTON.cancel}
onClick={handleCancel}
disabled={submitButtonState === 'pending'}
>
{BUTTON.cancel}
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,45 @@ import {
} from '@edx/paragon';
import PROVISIONING_PAGE_TEXT from '../data/constants';
import useProvisioningContext from '../data/hooks';
import { selectProvisioningContext } from '../data/utils';

const ProvisioningFormTitle = () => {
const { setSubsidyTitle } = useProvisioningContext();
const { FORM: { PLAN_TITLE } } = PROVISIONING_PAGE_TEXT;
const [showInvalidField] = selectProvisioningContext('showInvalidField');
const { setInvalidSubsidyFields } = useProvisioningContext();
const { subsidy } = showInvalidField;
const isSubsidyTitleDefinedAndFalse = subsidy?.subsidyTitle === false;

const handleChange = (e) => {
const newEventValue = e.target.value;
if (e.target.value === '') {
setInvalidSubsidyFields({ ...subsidy, subsidyTitle: false });
setSubsidyTitle('');
return;
}
setSubsidyTitle(newEventValue);
setInvalidSubsidyFields({ ...subsidy, subsidyTitle: true });
};
return (
<article className="mt-4.5">
<div className="mb-1">
<h3>{PLAN_TITLE.HEADER}</h3>
</div>
<Form.Group className="mt-3.5">
<Form.Group
className="mt-3.5"
isInvalid={isSubsidyTitleDefinedAndFalse}
>
<Form.Control
floatingLabel={PLAN_TITLE.TITLE}
onChange={e => setSubsidyTitle(e.target.value)}
onChange={handleChange}
data-testid="customer-plan-title"
/>
{isSubsidyTitleDefinedAndFalse && (
<Form.Control.Feedback type="invalid">
{PLAN_TITLE.ERROR}
</Form.Control.Feedback>
)}
</Form.Group>
</article>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ProvisioningContext, initialStateValue } from '../../../testData';
import PROVISIONING_PAGE_TEXT from '../../data/constants';
import useProvisioningContext from '../../data/hooks';
import ProvisioningFormAccountType from '../ProvisioningFormAccountType';
import ProvisioningFormTitle from '../ProvisioningFormTitle';

const { ACCOUNT_CREATION } = PROVISIONING_PAGE_TEXT.FORM;

Expand All @@ -20,18 +21,21 @@ jest.mock('react', () => ({
...jest.requireActual('react'),
useState: (initialState) => [initialState, mockUseState],
}));

global.scrollTo = jest.fn();
const mockHydrateCatalogQueryData = jest.fn();
useProvisioningContext.mockReturnValue({
setSubsidyTitle: jest.fn(),
setMultipleFunds: jest.fn(),
hydrateCatalogQueryData: mockHydrateCatalogQueryData,
setCustomCatalog: jest.fn(),
setAlertMessage: jest.fn(),
setInvalidSubsidyFields: jest.fn(),
});
const ProvisioningFormAccountTypeWrapper = ({
value = initialStateValue,
}) => (
<ProvisioningContext value={value}>
<ProvisioningFormTitle />
<ProvisioningFormAccountType />
</ProvisioningContext>
);
Expand Down Expand Up @@ -74,17 +78,69 @@ describe('ProvisioningFormAccountType', () => {

expect(screen.getByTestId(ACCOUNT_CREATION.OPTIONS.single)).toBeTruthy();
});
it('hydrates catalog query data', async () => {
it('hydrates catalog query data multiple', async () => {
const value = {
...initialStateValue,
catalogQueries: {
data: [],
isLoading: false,
},
formData: {
...initialStateValue.formData,
subsidyTitle: 'test',
},
};

renderWithRouter(<ProvisioningFormAccountTypeWrapper value={value} />);

// sets input value to 'test'
const multipleTestId = screen.getByTestId(ACCOUNT_CREATION.OPTIONS.multiple);
fireEvent.click(multipleTestId);
await waitFor(() => expect(mockHydrateCatalogQueryData).toHaveBeenCalled());
});
it('hydrates catalog query data single', async () => {
const value = {
...initialStateValue,
catalogQueries: {
data: [],
isLoading: false,
},
formData: {
...initialStateValue.formData,
subsidyTitle: 'test',
},
};

renderWithRouter(<ProvisioningFormAccountTypeWrapper value={value} />);

// sets input value to 'test'
const singleTestId = screen.getByTestId(ACCOUNT_CREATION.OPTIONS.single);
fireEvent.click(singleTestId);
await waitFor(() => expect(mockHydrateCatalogQueryData).toHaveBeenCalled());
});
it('handles error of hydrating catalog query data', async () => {
const value = {
...initialStateValue,
catalogQueries: {
data: [],
isLoading: false,
},
formData: {
...initialStateValue.formData,
subsidyTitle: 'test',
},
};
const error = new Error('test');
error.customAttributes = {
httpErrorStatus: 500,
};
mockHydrateCatalogQueryData.mockImplementation(() => {
throw error;
});

renderWithRouter(<ProvisioningFormAccountTypeWrapper value={value} />);

// sets input value to 'test'
const multipleTestId = screen.getByTestId(ACCOUNT_CREATION.OPTIONS.multiple);
fireEvent.click(multipleTestId);
await waitFor(() => expect(mockHydrateCatalogQueryData).toHaveBeenCalled());
Expand Down
Loading

0 comments on commit 45530bb

Please sign in to comment.