Skip to content

Commit

Permalink
Merge pull request #30833 from adhorodyski/chore/testing-utils
Browse files Browse the repository at this point in the history
[NoQA] chore(tests): unified utilities for mocking collections
  • Loading branch information
amyevans authored Nov 8, 2023
2 parents e1c98cb + 56a1e16 commit c473b17
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 0 deletions.
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: {},
};
}
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(),
};
}

0 comments on commit c473b17

Please sign in to comment.