Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NoQA] chore(tests): unified utilities for mocking collections #30833

Merged
merged 11 commits into from
Nov 8, 2023
21 changes: 21 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
"@dword-design/eslint-plugin-import-alias": "^4.0.8",
"@electron/notarize": "^2.1.0",
"@jest/globals": "^29.5.0",
"@ngneat/falso": "^7.1.1",
"@octokit/core": "4.0.4",
"@octokit/plugin-paginate-rest": "3.1.0",
"@octokit/plugin-throttling": "4.1.0",
Expand Down
30 changes: 30 additions & 0 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,36 @@
- To simulate a network request succeeding or failing we can mock the expected response first and then manually trigger the action that calls that API command.
- [Mocking the response of `HttpUtils.xhr()`](https://github.com/Expensify/App/blob/ca2fa88a5789b82463d35eddc3d57f70a7286868/tests/actions/SessionTest.js#L25-L32) is the best way to simulate various API conditions so we can verify whether a result occurs or not.

## Mocking collections / collection items

When unit testing an interface with Jest/performance testing with Reassure you might need to work with collections of data. These often get tricky to generate and maintain. To help with this we have a few helper methods located in `tests/utils/collections/`.

- `createCollection()` - Creates a collection of data (`Record<string, T>`) with a given number of items (default=500). This is useful for eg. testing the performance of a component with a large number of items. You can use it to populate Onyx.
- `createRandom*()` - like `createRandomPolicy`, these functions are responsible for generating a randomised object of the given type. You can use them as your defaults when calling `createCollection()` or as standalone utilities.

Basic example:
```ts
const policies = createCollection<Policy>(item => `policies_${item.id}`, createRandomPolicy);

/**
Output:
{
"policies_0": policyItem0,
"policies_1": policyItem1,
...
}
*/
```

Example with overrides:

```ts
const policies = createCollection<Policy>(
item => `policies_${item.id}`,
index => ({ ...createRandomPolicy(index), isPinned: true })
);
```

## Mocking `node_modules`, user modules, and what belongs in `jest/setup.js`

If you need to mock a library that exists in `node_modules` then add it to the `__mocks__` folder in the root of the project. More information about this [here](https://jestjs.io/docs/manual-mocks#mocking-node-modules). If you need to mock an individual library you should create a mock module in a `__mocks__` subdirectory adjacent to the library as explained [here](https://jestjs.io/docs/manual-mocks#mocking-user-modules). However, keep in mind that when you do this you also must manually require the mock by calling something like `jest.mock('../../src/libs/Log');` at the top of an individual test file. If every test in the app will need something to be mocked that's a good case for adding it to `jest/setup.js`, but we should generally avoid adding user mocks or `node_modules` mocks to this file. Please use the `__mocks__` subdirectories wherever appropriate.
Expand Down
11 changes: 11 additions & 0 deletions tests/utils/collections/createCollection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function createCollection<T>(createKey: (item: T, index: number) => string, createItem: (index: number) => T, length = 500): Record<string, T> {
const map: Record<string, T> = {};

for (let i = 0; i < length; i++) {
const item = createItem(i);
const itemKey = createKey(item, i);
map[itemKey] = item;
}

return map;
}
26 changes: 26 additions & 0 deletions tests/utils/collections/policies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {rand, randAvatar, randBoolean, randCurrencyCode, randEmail, randPastDate, randWord} from '@ngneat/falso';
import CONST from '@src/CONST';
import type {Policy} from '@src/types/onyx';

export default function createRandomPolicy(index: number): Policy {
return {
id: index.toString(),
name: randWord(),
type: rand(Object.values(CONST.POLICY.TYPE)),
areChatRoomsEnabled: randBoolean(),
autoReporting: randBoolean(),
isPolicyExpenseChatEnabled: randBoolean(),
autoReportingFrequency: rand(Object.values(CONST.POLICY.AUTO_REPORTING_FREQUENCIES)),
outputCurrency: randCurrencyCode(),
role: rand(Object.values(CONST.POLICY.ROLE)),
owner: randEmail(),
ownerAccountID: index,
avatar: randAvatar(),
isFromFullPolicy: randBoolean(),
lastModified: randPastDate().toISOString(),
pendingAction: rand(Object.values(CONST.RED_BRICK_ROAD_PENDING_ACTION)),
errors: {},
customUnits: {},
errorFields: {},
adhorodyski marked this conversation as resolved.
Show resolved Hide resolved
};
}
22 changes: 22 additions & 0 deletions tests/utils/collections/reports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {rand, randBoolean, randCurrencyCode, randEmail, randWord} from '@ngneat/falso';
import CONST from '@src/CONST';
import type {Report} from '@src/types/onyx';

export default function createRandomReport(index: number): Report {
return {
reportID: index.toString(),
chatType: rand(Object.values(CONST.REPORT.CHAT_TYPE)),
currency: randCurrencyCode(),
displayName: randWord(),
hasDraft: randBoolean(),
ownerEmail: randEmail(),
ownerAccountID: index,
isPinned: randBoolean(),
isOptimisticReport: randBoolean(),
isOwnPolicyExpenseChat: randBoolean(),
isWaitingOnBankAccount: randBoolean(),
isLastMessageDeletedParentAction: randBoolean(),
policyID: index.toString(),
reportName: randWord(),
};
}
Loading