* Add autotranslations
* Develop latest changes and missing features
* i18n-extract
* Fix tests and update api behavior
* Fix minor bugs
* adjustments to shimmer animation, showtranslation modal style
* Update post.tsx
* Update show_translation.tsx
* adjust sizing and opacity of translate icon
* Add channel header translated icon
* Disable auto translation item if it is not a supported language
* Move channel info to the top
* Update edit channel to channel info
* Fix align of option items
* Fix i18n
* Add detox related changes for channel settings changes
* Add tests
* Address changes around the my channel column change
* Address feedback
* Fix test
* Fix bad import
* Fix set my channel autotranslation
* Fix test
* Address feedback
* Add missing files from last commit
* Remove unneeded change
---------
(cherry picked from commit 23565b5135)
Co-authored-by: Daniel Espino García <larkox@gmail.com>
Co-authored-by: Matthew Birtch <mattbirtch@gmail.com>
6.7 KiB
6.7 KiB
Unit testing guide
This document describes how to add and structure unit tests in the Mattermost Mobile project. For high-level testing notes and patterns, see the Testing section in CLAUDE.md. For concrete examples, see app/products/playbooks, which is the canonical reference for test layout and patterns.
Where tests live
- Tests are co-located with source files:
*.test.tsor*.test.tsxnext to the file under test (e.g.channel.test.tsbesidechannel.ts). - Screen entry points often have an
index.test.tsxthat tests the wrapper and passes through to the main screen component. - Reference: app/products/playbooks for the full pattern.
What to test by layer
Local actions (app/actions/local/)
- Use a real in-memory database:
DatabaseManager.init([serverUrl])inbeforeEachandDatabaseManager.destroyServerDatabase(serverUrl)inafterEach. - Use TestHelper for fake entities:
TestHelper.fakeChannel(),TestHelper.fakePost(),TestHelper.fakeChannelMember(), etc. - Cover: not-found database (invalid
serverUrl), not-found entity (e.g. channel/post missing), success path, and error path (e.g. mockdatabase.writeoroperator.batchRecordsto throw). - Example: app/products/playbooks/actions/local/channel.test.ts, app/actions/local/post.test.ts.
Remote actions (app/actions/remote/)
- Mock
NetworkManager.getClientinbeforeAllto return a client with the methods used (e.g.setChannelAutotranslation,setMyChannelAutotranslation). - Assert the client was called with the expected arguments and that the action returns
{data}on success and{error}on failure. - On error, assert
forceLogoutIfNecessary(and any logging) is called when applicable. - Example: app/products/playbooks/actions/remote/playbooks.test.ts, app/actions/remote/channel.test.ts.
Queries (app/queries/servers/)
- For query functions that return a WatermelonDB
Query: use a real DB, create the relevant records with the operator, run the query, and assert on the fetched results. - For observe functions that return RxJS observables (canonical pattern from playbooks):
- Use a real database:
DatabaseManager.init([serverUrl])inbeforeEach, destroy inafterEach. - Set up data in the DB with the operator (e.g.
handleChannel,handleMyChannel,handleSystem) so the observe under test has real records to emit. - If the observe function depends on other observables from a mocked module (e.g.
observeConfigBooleanValuefrom./system), mock that dependency to return an Observable (e.g.jest.mocked(observeConfigBooleanValue).mockReturnValue(of$(true))) socombineLatestand similar operators receive valid observables. - Call the observe function with
database(and any ids), then subscribe with a spy:const subscriptionNext = jest.fn(); result.subscribe({ next: subscriptionNext }); - Assert the emitted value:
expect(subscriptionNext).toHaveBeenCalledWith(expectedValue).
- Use a real database:
- You can also use
firstValueFrom(observable)to await the first emission when that is clearer. - Example: app/products/playbooks/database/queries/run.test.ts (
observePlaybookRunById,observePlaybookRunProgress), app/products/playbooks/database/queries/version.test.ts (observeIsPlaybooksEnabled), app/queries/servers/channel.test.ts (observeChannelAutotranslation,observeMyChannelAutotranslation,observeIsChannelAutotranslated).
Screens and UI
- Use
renderWithEverythingfrom@test/intl-test-helperwhen the component needs database or server URL (pass{database}and optionallyserverUrl). UserenderWithIntlAndThemewhen it only needs theme/intl (no DB). - Mock heavy or external deps: navigation, remote actions,
useServerUrl, etc., withjest.mock(...). Mock child components withmockImplementation((props) => React.createElement('ComponentName', { testID: '...', ...props }))so they render with a stabletestIDand forward props. - Asserting on props passed to children: Query the screen for the rendered element (e.g.
getByTestId('...')orgetAllByTestId('...')), then assert withexpect(element).toHaveProp('propName', value). Do not usejest.mocked(Component).mock.callsormock.calls[0][0]to inspect props—assert on what is in the tree, as in playbooks screen tests. - Assert that key elements render (e.g. by
testID) and that user actions (e.g. toggle, button press) call the expected handlers or actions. - Use
fireEvent.press()for button/toggle interactions; wrap async updates inact()orwaitForwhen needed. - Use a
getBaseProps()helper typed asComponentProps<typeof Component>for default props andbeforeEach(() => jest.clearAllMocks())where appropriate. - Example: app/products/playbooks/screens/select_user/select_user.test.tsx, app/screens/show_translation/show_translation.test.tsx.
Client REST (app/client/rest/)
- Create the client (e.g.
TestHelper.createClient()), then mockclient.doFetch. - Assert the request URL (and query string if any) and options (method, body, etc.), and that the return value or thrown error matches expectations.
- Example: app/products/playbooks/client/rest.test.ts, app/client/rest/channels.test.ts.
Helpers and setup
- TestHelper (
test/test_helper.ts):fakeChannel,fakePost,fakeUser,fakeChannelMember, etc.;createClient()for REST client tests. - Database:
DatabaseManager.init([serverUrl])andDatabaseManager.destroyServerDatabase(serverUrl); getdatabaseandoperatorfromDatabaseManager.getServerDatabaseAndOperator(serverUrl). - Rendering:
renderWithEverything(ui, { database, serverUrl })when the component needs DB or server context. - Events:
DeviceEventEmitter.addListener(Events.SOME_EVENT, callback)to assert that an event was emitted; calllistener.remove()in cleanup.
Jest configuration
- Setup:
test/setup.tsis run before tests; avoid adding one-off mocks there. - Coverage:
coveragePathIgnorePatternsinjest.config.jsexclude/components/and/screens/, but tests for critical screens and components are still encouraged for regression safety.