Skip to content

Commit

Permalink
Merge pull request #37531 from VickyStash/ts-migration/tests-g10
Browse files Browse the repository at this point in the history
[TS migration] Migrate 'SelectionList.perf-test.js', 'ReportActionItemSingleTest.js', 'LoginUtilsTest.js' and 'isEmptyString.js' tests to TypeScript
  • Loading branch information
thienlnam authored Mar 8, 2024
2 parents 369cbe2 + 4676abb commit d9aab7c
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 46 deletions.
19 changes: 19 additions & 0 deletions src/types/utils/CollectionDataSet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
import type {OnyxEntry} from 'react-native-onyx';
import type {OnyxCollectionKey, OnyxCollectionValuesMapping} from '@src/ONYXKEYS';

/** Helps with typing a collection item update inside Onyx.multiSet call */
type CollectionDataSet<TCollectionKey extends OnyxCollectionKey> = Record<`${TCollectionKey}${string}`, OnyxCollectionValuesMapping[TCollectionKey]>;

const toCollectionDataSet = <TCollectionKey extends OnyxCollectionKey>(
collectionKey: TCollectionKey,
collection: Array<OnyxEntry<OnyxCollectionValuesMapping[TCollectionKey]>>,
idSelector: (collectionValue: OnyxCollectionValuesMapping[TCollectionKey]) => string,
) => {
const collectionDataSet = collection.reduce<CollectionDataSet<TCollectionKey>>((result, collectionValue) => {
if (collectionValue) {
// eslint-disable-next-line no-param-reassign
result[`${collectionKey}${idSelector(collectionValue)}`] = collectionValue;
}
return result;
}, {} as CollectionDataSet<TCollectionKey>);

return collectionDataSet;
};

export default CollectionDataSet;

export {toCollectionDataSet};
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
import {fireEvent} from '@testing-library/react-native';
import type {RenderResult} from '@testing-library/react-native';
import React, {useState} from 'react';
import type {ComponentType} from 'react';
import {measurePerformance} from 'reassure';
import _ from 'underscore';
import SelectionList from '@components/SelectionList';
import RadioListItem from '@components/SelectionList/RadioListItem';
import SelectionList from '../../src/components/SelectionList';
import variables from '../../src/styles/variables';
import type {ListItem} from '@components/SelectionList/types';
import type {KeyboardStateContextValue} from '@components/withKeyboardState';
import type {WithLocalizeProps} from '@components/withLocalize';
import variables from '@styles/variables';

jest.mock('../../src/components/Icon/Expensicons');
type SelectionListWrapperProps = {
/** Whether this is a multi-select list */
canSelectMultiple?: boolean;
};

jest.mock('../../src/hooks/useLocalize', () =>
jest.mock('@components/Icon/Expensicons');

jest.mock('@hooks/useLocalize', () =>
jest.fn(() => ({
translate: jest.fn(),
})),
);

jest.mock('../../src/components/withLocalize', () => (Component) => {
function WrappedComponent(props) {
jest.mock('@components/withLocalize', <TProps extends WithLocalizeProps>() => (Component: ComponentType<TProps>) => {
function WrappedComponent(props: Omit<TProps, keyof WithLocalizeProps>) {
return (
<Component
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
{...(props as TProps)}
translate={() => ''}
/>
);
Expand All @@ -28,18 +37,18 @@ jest.mock('../../src/components/withLocalize', () => (Component) => {
return WrappedComponent;
});

jest.mock('../../src/hooks/useNetwork', () =>
jest.mock('@hooks/useNetwork', () =>
jest.fn(() => ({
isOffline: false,
})),
);

jest.mock('../../src/components/withKeyboardState', () => (Component) => {
function WrappedComponent(props) {
jest.mock('@components/withKeyboardState', () => <TProps extends KeyboardStateContextValue>(Component: ComponentType<TProps>) => {
function WrappedComponent(props: Omit<TProps, keyof KeyboardStateContextValue>) {
return (
<Component
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
{...(props as TProps)}
isKeyboardShown={false}
/>
);
Expand All @@ -54,25 +63,25 @@ jest.mock('@react-navigation/native', () => ({
createNavigationContainerRef: jest.fn(),
}));

function SelectionListWrapper(args) {
const [selectedIds, setSelectedIds] = useState([]);
function SelectionListWrapper({canSelectMultiple}: SelectionListWrapperProps) {
const [selectedIds, setSelectedIds] = useState<string[]>([]);

const sections = [
{
data: Array.from({length: 1000}, (__, i) => ({
text: `Item ${i}`,
keyForList: `item-${i}`,
isSelected: _.contains(selectedIds, `item-${i}`),
data: Array.from({length: 1000}, (element, index) => ({
text: `Item ${index}`,
keyForList: `item-${index}`,
isSelected: selectedIds.includes(`item-${index}`),
})),
indexOffset: 0,
isDisabled: false,
},
];

const onSelectRow = (item) => {
if (args.canSelectMultiple) {
if (_.contains(selectedIds, item.keyForList)) {
setSelectedIds(_.without(selectedIds, item.keyForList));
const onSelectRow = (item: ListItem) => {
if (canSelectMultiple) {
if (selectedIds.includes(item.keyForList)) {
setSelectedIds(selectedIds.filter((selectedId) => selectedId === item.keyForList));
} else {
setSelectedIds([...selectedIds, item.keyForList]);
}
Expand All @@ -88,8 +97,7 @@ function SelectionListWrapper(args) {
onSelectRow={onSelectRow}
initiallyFocusedOptionKey="item-0"
ListItem={RadioListItem}
// eslint-disable-next-line react/jsx-props-no-spreading
{...args}
canSelectMultiple={canSelectMultiple}
/>
);
}
Expand All @@ -99,15 +107,17 @@ test('[SelectionList] should render 1 section and a thousand items', () => {
});

test('[SelectionList] should press a list item', () => {
const scenario = (screen) => {
// eslint-disable-next-line @typescript-eslint/require-await
const scenario = async (screen: RenderResult) => {
fireEvent.press(screen.getByText('Item 5'));
};

measurePerformance(<SelectionListWrapper />, {scenario});
});

test('[SelectionList] should render multiple selection and select 3 items', () => {
const scenario = (screen) => {
// eslint-disable-next-line @typescript-eslint/require-await
const scenario = async (screen: RenderResult) => {
fireEvent.press(screen.getByText('Item 1'));
fireEvent.press(screen.getByText('Item 2'));
fireEvent.press(screen.getByText('Item 3'));
Expand Down Expand Up @@ -135,7 +145,8 @@ test('[SelectionList] should scroll and select a few items', () => {
},
};

const scenario = (screen) => {
// eslint-disable-next-line @typescript-eslint/require-await
const scenario = async (screen: RenderResult) => {
fireEvent.press(screen.getByText('Item 1'));
// see https://github.com/callstack/react-native-testing-library/issues/1540
fireEvent(screen.getByTestId('selection-list'), 'onContentSizeChange', eventData.nativeEvent.contentSize.width, eventData.nativeEvent.contentSize.height);
Expand Down
4 changes: 2 additions & 2 deletions tests/unit/LoginUtilsTest.js → tests/unit/LoginUtilsTest.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Onyx from 'react-native-onyx';
import * as LoginUtils from '../../src/libs/LoginUtils';
import ONYXKEYS from '../../src/ONYXKEYS';
import * as LoginUtils from '@libs/LoginUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';

describe('LoginUtils', () => {
Expand Down
12 changes: 7 additions & 5 deletions tests/unit/NextStepUtilsTest.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import {format, lastDayOfMonth, setDate} from 'date-fns';
import Onyx from 'react-native-onyx';
import DateUtils from '@libs/DateUtils';
import * as NextStepUtils from '@libs/NextStepUtils';
import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Policy, Report, ReportNextStep} from '@src/types/onyx';
import DateUtils from '../../src/libs/DateUtils';
import * as NextStepUtils from '../../src/libs/NextStepUtils';
import * as ReportUtils from '../../src/libs/ReportUtils';
import {toCollectionDataSet} from '@src/types/utils/CollectionDataSet';
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';

Onyx.init({keys: ONYXKEYS});
Expand Down Expand Up @@ -40,17 +41,18 @@ describe('libs/NextStepUtils', () => {
const report = ReportUtils.buildOptimisticExpenseReport('fake-chat-report-id-1', policyID, 1, -500, CONST.CURRENCY.USD) as Report;

beforeAll(() => {
// @ts-expect-error Preset necessary values
const policyCollectionDataSet = toCollectionDataSet(ONYXKEYS.COLLECTION.POLICY, [policy], (item) => item.id);

Onyx.multiSet({
[ONYXKEYS.SESSION]: {email: currentUserEmail, accountID: currentUserAccountID},
[`${ONYXKEYS.COLLECTION.POLICY}${policyID}`]: policy,
[ONYXKEYS.PERSONAL_DETAILS_LIST]: {
[strangeAccountID]: {
accountID: strangeAccountID,
login: strangeEmail,
avatar: '',
},
},
...policyCollectionDataSet,
}).then(waitForBatchedUpdates);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {cleanup, screen, waitFor} from '@testing-library/react-native';
import Onyx from 'react-native-onyx';
import type {PersonalDetailsList} from '@src/types/onyx';
import {toCollectionDataSet} from '@src/types/utils/CollectionDataSet';
import * as LHNTestUtils from '../utils/LHNTestUtils';
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates';
Expand All @@ -12,13 +14,12 @@ const ONYXKEYS = {
POLICY: 'policy_',
},
NETWORK: 'network',
};
} as const;

describe('ReportActionItemSingle', () => {
beforeAll(() =>
Onyx.init({
keys: ONYXKEYS,
registerStorageEventListener: () => {},
safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
}),
);
Expand All @@ -42,9 +43,10 @@ describe('ReportActionItemSingle', () => {
const fakeReport = LHNTestUtils.getFakeReportWithPolicy([1, 2]);
const fakeReportAction = LHNTestUtils.getFakeAdvancedReportAction();
const fakePolicy = LHNTestUtils.getFakePolicy(fakeReport.policyID);
const fakePersonalDetails = {
[fakeReportAction.actorAccountID]: {
accountID: fakeReportAction.actorAccountID,
const faceAccountId = fakeReportAction.actorAccountID ?? -1;
const fakePersonalDetails: PersonalDetailsList = {
[faceAccountId]: {
accountID: faceAccountId,
login: '[email protected]',
displayName: 'Email One',
avatar: 'https://example.com/avatar.png',
Expand All @@ -57,11 +59,13 @@ describe('ReportActionItemSingle', () => {
});

function setup() {
const policyCollectionDataSet = toCollectionDataSet(ONYXKEYS.COLLECTION.POLICY, [fakePolicy], (item) => item.id);

return waitForBatchedUpdates().then(() =>
Onyx.multiSet({
[ONYXKEYS.PERSONAL_DETAILS_LIST]: fakePersonalDetails,
[ONYXKEYS.IS_LOADING_REPORT_DATA]: false,
[`${ONYXKEYS.COLLECTION.POLICY}${fakeReport.policyID}`]: fakePolicy,
...policyCollectionDataSet,
}),
);
}
Expand All @@ -77,10 +81,10 @@ describe('ReportActionItemSingle', () => {
});

it('renders Person information', () => {
const [expectedPerson] = fakeReportAction.person;
const [expectedPerson] = fakeReportAction.person ?? [];

return setup().then(() => {
expect(screen.getByText(expectedPerson.text)).toBeDefined();
expect(screen.getByText(expectedPerson.text ?? '')).toBeDefined();
});
});
});
Expand Down
7 changes: 3 additions & 4 deletions tests/unit/isEmptyString.js → tests/unit/isEmptyString.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import _ from 'underscore';
import enEmojis from '../../assets/emojis/en';
import StringUtils from '../../src/libs/StringUtils';
import enEmojis from '@assets/emojis/en';
import StringUtils from '@libs/StringUtils';

describe('libs/StringUtils.isEmptyString', () => {
it('basic tests', () => {
Expand Down Expand Up @@ -50,7 +49,7 @@ describe('libs/StringUtils.isEmptyString', () => {
expect(StringUtils.isEmptyString('😀')).toBe(false);
});
it('all emojis not removed', () => {
_.keys(enEmojis).forEach((key) => {
Object.keys(enEmojis).forEach((key) => {
expect(StringUtils.isEmptyString(key)).toBe(false);
});
});
Expand Down

0 comments on commit d9aab7c

Please sign in to comment.