Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,10 @@ function TransactionGroupListItem<TItem extends ListItem>({

const transactionsWithoutPendingDelete = transactions.filter((transaction) => transaction.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE);

const isEmpty = transactions.length === 0;
// A group whose children are lazily loaded (it has a transactionsQueryJSON) is not empty, it just hasn't been fetched yet
const isEmpty = transactions.length === 0 && groupItem.transactions.length === 0 && !groupItem.transactionsQueryJSON;

const isEmptyReportSelected = isEmpty && item?.keyForList && selectedTransactions[item.keyForList]?.isSelected;
const isEmptyReportSelected = transactions.length === 0 && item?.keyForList && selectedTransactions[item.keyForList]?.isSelected;
Comment thread
lorretheboy marked this conversation as resolved.
Outdated

const isSelectAllChecked = isEmptyReportSelected || (selectedItemsLength === transactionsWithoutPendingDelete.length && transactionsWithoutPendingDelete.length > 0);
const isIndeterminate = selectedItemsLength > 0 && selectedItemsLength !== transactionsWithoutPendingDelete.length;
Expand Down
97 changes: 96 additions & 1 deletion tests/unit/TransactionGroupListItemTest.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ import {LocaleContextProvider} from '@components/LocaleContextProvider';
import OnyxListItemProvider from '@components/OnyxListItemProvider';
import ScreenWrapper from '@components/ScreenWrapper';
import {SearchContextProvider} from '@components/Search/SearchContextProvider';
import type {TransactionGroupListItemProps, TransactionListItemType, TransactionReportGroupListItemType} from '@components/Search/SearchList/ListItem/types';
import type {
TransactionCategoryGroupListItemType,
TransactionGroupListItemProps,
TransactionListItemType,
TransactionReportGroupListItemType,
} from '@components/Search/SearchList/ListItem/types';

import {buildSearchQueryJSON} from '@libs/SearchQueryUtils';

import TransactionGroupListItem from '@src/components/Search/SearchList/ListItem/TransactionGroupListItem';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -633,3 +640,91 @@ describe('Empty Report Selection', () => {
expect(collapsibleContent).toBeNull();
});
});

describe('Lazily loaded group selection', () => {
const mockOnSelectRow = jest.fn();
const mockOnCheckboxPress = jest.fn();

// A `group-by:category` group whose child transactions are only fetched once the group is expanded,
// so `transactions` is still empty on first render even though the group is not actually empty.
const mockCategoryGroup: TransactionCategoryGroupListItemType = {
groupedBy: CONST.SEARCH.GROUP_BY.CATEGORY,
category: 'Advertising',
formattedCategory: 'Advertising',
count: 3,
total: -1284,
currency: 'USD',
transactions: [],
transactionsQueryJSON: buildSearchQueryJSON('type:expense category:Advertising'),
keyForList: 'Advertising',
};

beforeAll(() => {
Onyx.init({
keys: ONYXKEYS,
evictableKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
});
jest.spyOn(NativeNavigation, 'useRoute').mockReturnValue({key: '', name: ''});
});

beforeEach(() => {
jest.clearAllMocks();
return act(async () => {
await Onyx.clear();
await waitForBatchedUpdatesWithAct();
});
});

const defaultProps: TransactionGroupListItemProps<TransactionCategoryGroupListItemType> = {
item: mockCategoryGroup,
showTooltip: false,
onSelectRow: mockOnSelectRow,
onSelectionButtonPress: mockOnCheckboxPress,
searchType: CONST.SEARCH.DATA_TYPES.EXPENSE,
groupBy: CONST.SEARCH.GROUP_BY.CATEGORY,
canSelectMultiple: true,
keyForList: 'Advertising',
};

function TestWrapper({children}: {children: React.ReactNode}) {
return (
<ComposeProviders components={[OnyxListItemProvider, LocaleContextProvider]}>
<ScreenWrapper testID="test">
<SearchContextProvider>{children}</SearchContextProvider>
</ScreenWrapper>
</ComposeProviders>
);
}

const renderCategoryGroup = () => render(<TransactionGroupListItem {...defaultProps} />, {wrapper: TestWrapper});

it('should select the group when tapping the checkbox before the group has been expanded', async () => {
renderCategoryGroup();
await waitForBatchedUpdatesWithAct();

// The checkbox of a group whose transactions have not been fetched yet should still be enabled
const checkbox = screen.getByRole(CONST.ROLE.CHECKBOX);
expect(checkbox).not.toBeDisabled();

// When pressing it
fireEvent.press(checkbox);
await waitForBatchedUpdatesWithAct();

// Then the group should be selected
expect(mockOnCheckboxPress).toHaveBeenCalledTimes(1);
expect(mockOnCheckboxPress).toHaveBeenCalledWith(mockCategoryGroup, []);
});

it('should expand the group instead of selecting it when tapping the expand arrow', async () => {
renderCategoryGroup();
await waitForBatchedUpdatesWithAct();

// When pressing the expand arrow of a group whose transactions have not been fetched yet
fireEvent.press(screen.getByLabelText('Expand'));
await waitForBatchedUpdatesWithAct();

// Then the group should expand, and not be selected
expect(screen.getByLabelText('Collapse')).toBeTruthy();
expect(mockOnSelectRow).not.toHaveBeenCalled();
});
});
Loading