-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ import * as MainQueue from '@src/libs/Network/MainQueue'; | |
import * as NetworkStore from '@src/libs/Network/NetworkStore'; | ||
import NetworkConnection from '@src/libs/NetworkConnection'; | ||
import ONYXKEYS from '@src/ONYXKEYS'; | ||
import type {Session as OnyxSession} from '@src/types/onyx'; | ||
import type {Network as OnyNetwork, Session as OnyxSession} from '@src/types/onyx'; | ||
import type ReactNativeOnyxMock from '../../__mocks__/react-native-onyx'; | ||
import * as TestHelper from '../utils/TestHelper'; | ||
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; | ||
|
@@ -132,81 +132,95 @@ describe('NetworkTests', () => { | |
}); | ||
}); | ||
}); | ||
|
||
test('failing to reauthenticate while offline should not log out user', async () => { | ||
const TEST_USER_LOGIN = '[email protected]'; | ||
const TEST_USER_ACCOUNT_ID = 1; | ||
|
||
let session: OnyxEntry<OnyxSession>; | ||
Onyx.connect({ | ||
key: ONYXKEYS.SESSION, | ||
callback: (val) => (session = val), | ||
}); | ||
|
||
Check failure on line 136 in tests/unit/NetworkTest.ts GitHub Actions / ESLint check
|
||
Onyx.connect({ | ||
key: ONYXKEYS.NETWORK, | ||
// 1. Setup Phase - Initialize test user and state variables | ||
const TEST_USER_LOGIN = '[email protected]'; | ||
Check failure on line 138 in tests/unit/NetworkTest.ts GitHub Actions / ESLint check
|
||
const TEST_USER_ACCOUNT_ID = 1; | ||
Check failure on line 139 in tests/unit/NetworkTest.ts GitHub Actions / ESLint check
|
||
const defaultTimeout = 1000; | ||
Check failure on line 140 in tests/unit/NetworkTest.ts GitHub Actions / ESLint check
|
||
|
||
Check failure on line 141 in tests/unit/NetworkTest.ts GitHub Actions / ESLint check
|
||
let sessionState: OnyxEntry<OnyxSession>; | ||
Check failure on line 142 in tests/unit/NetworkTest.ts GitHub Actions / ESLint check
|
||
let networkState: OnyxEntry<OnyNetwork>; | ||
Check failure on line 143 in tests/unit/NetworkTest.ts GitHub Actions / ESLint check
|
||
|
||
Check failure on line 144 in tests/unit/NetworkTest.ts GitHub Actions / ESLint check
|
||
// Set up listeners for session and network state changes | ||
Check failure on line 145 in tests/unit/NetworkTest.ts GitHub Actions / ESLint check
|
||
Onyx.connect({ | ||
Check failure on line 146 in tests/unit/NetworkTest.ts GitHub Actions / ESLint check
|
||
key: ONYXKEYS.SESSION, | ||
callback: (val) => (sessionState = val), | ||
}); | ||
|
||
Onyx.connect({ | ||
key: ONYXKEYS.NETWORK, | ||
callback: (val) => (networkState = val), | ||
}); | ||
|
||
// Sign in test user and wait for updates | ||
await TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN); | ||
await waitForBatchedUpdates(); | ||
|
||
const initialAuthToken = sessionState?.authToken; | ||
expect(initialAuthToken).toBeDefined(); | ||
|
||
// 2. Mock Setup Phase - Configure XHR mocks for the test sequence | ||
const mockedXhr = jest | ||
.fn() | ||
// First mock: ReconnectApp will fail with NOT_AUTHENTICATED | ||
.mockImplementationOnce(() => | ||
Promise.resolve({ | ||
jsonCode: CONST.JSON_CODE.NOT_AUTHENTICATED, | ||
}), | ||
) | ||
// Second mock: Authenticate with network check and delay | ||
.mockImplementationOnce(() => { | ||
// Check network state immediately | ||
if (networkState?.isOffline) { | ||
return Promise.reject(new Error('Network request failed')); | ||
} | ||
|
||
// create a delayed response. Timeout will expire after the second App.reconnectApp(); | ||
return new Promise((_, reject) => { | ||
setTimeout(() => { | ||
reject(new Error('Network request failed')); | ||
}, defaultTimeout); | ||
}); | ||
}); | ||
|
||
HttpUtils.xhr = mockedXhr; | ||
|
||
// 3. Test Execution Phase - Start with online network | ||
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false}); | ||
|
||
// Trigger reconnect which will fail due to expired token | ||
App.confirmReadyToOpenApp(); | ||
App.reconnectApp(); | ||
await waitForBatchedUpdates(); | ||
|
||
// 4. First API Call Verification - Check ReconnectApp | ||
const firstCall = mockedXhr.mock.calls.at(0) as [string, Record<string, unknown>]; | ||
expect(firstCall[0]).toBe('ReconnectApp'); | ||
|
||
// 5. Authentication Start - Verify authenticate was triggered | ||
await waitForBatchedUpdates(); | ||
const secondCall = mockedXhr.mock.calls.at(1) as [string, Record<string, unknown>]; | ||
expect(secondCall[0]).toBe('Authenticate'); | ||
|
||
// 6. Network State Change - Set offline and back online while authenticate is pending | ||
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: true}); | ||
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false}); | ||
|
||
// Trigger another reconnect due to network change | ||
App.confirmReadyToOpenApp(); | ||
App.reconnectApp(); | ||
await waitForBatchedUpdates(); | ||
|
||
// 7. Wait and Verify - Wait for authenticate timeout and verify session | ||
await new Promise((resolve) => { | ||
setTimeout(resolve, defaultTimeout + 100); | ||
}); | ||
|
||
// Verify the session remained intact and wasn't cleared | ||
expect(sessionState?.authToken).toBe(initialAuthToken); | ||
}); | ||
|
||
await TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN); | ||
await waitForBatchedUpdates(); | ||
|
||
expect(session?.authToken).not.toBeUndefined(); | ||
|
||
// Turn off the network | ||
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: true}); | ||
|
||
const mockedXhr = jest.fn(); | ||
mockedXhr | ||
// Call ReconnectApp with an expired token | ||
.mockImplementationOnce(() => | ||
Promise.resolve({ | ||
jsonCode: CONST.JSON_CODE.NOT_AUTHENTICATED, | ||
}), | ||
) | ||
// Call Authenticate | ||
.mockImplementationOnce(() => | ||
Promise.resolve({ | ||
jsonCode: CONST.JSON_CODE.SUCCESS, | ||
authToken: 'newAuthToken', | ||
}), | ||
) | ||
// Call ReconnectApp again, it should connect with a new token | ||
.mockImplementationOnce(() => | ||
Promise.resolve({ | ||
jsonCode: CONST.JSON_CODE.SUCCESS, | ||
}), | ||
); | ||
|
||
HttpUtils.xhr = mockedXhr; | ||
|
||
// Initiate the requests | ||
App.confirmReadyToOpenApp(); | ||
App.reconnectApp(); | ||
await waitForBatchedUpdates(); | ||
|
||
// Turn the network back online | ||
await Onyx.set(ONYXKEYS.NETWORK, {isOffline: false}); | ||
|
||
// Filter requests results by request name | ||
const reconnectResults = (HttpUtils.xhr as Mock).mock.results.filter((_, index) => (HttpUtils.xhr as Mock)?.mock?.calls?.at(index)?.[0] === 'ReconnectApp'); | ||
const authenticateResults = (HttpUtils.xhr as Mock).mock.results.filter((_, index) => (HttpUtils.xhr as Mock)?.mock?.calls?.at(index)?.[0] === 'Authenticate'); | ||
|
||
// Get the response code of Authenticate call | ||
const authenticateResponse = await (authenticateResults?.at(0)?.value as Promise<{jsonCode: string}>); | ||
|
||
// Get the response code of the second Reconnect call | ||
const reconnectResponse = await (reconnectResults?.at(1)?.value as Promise<{jsonCode: string}>); | ||
|
||
// Authenticate request should return 200 | ||
expect(authenticateResponse.jsonCode).toBe(CONST.JSON_CODE.SUCCESS); | ||
|
||
// The second ReconnectApp should return 200 | ||
expect(reconnectResponse.jsonCode).toBe(CONST.JSON_CODE.SUCCESS); | ||
|
||
// check if the user is still logged in | ||
expect(session?.authToken).not.toBeUndefined(); | ||
}); | ||
|
||
test('consecutive API calls eventually succeed when authToken is expired', () => { | ||
// Given a test user login and account ID | ||
const TEST_USER_LOGIN = '[email protected]'; | ||
|