Skip to content

Commit 66c8c1d

Browse files
authored
Merge pull request Expensify#85288 from imgyf/imgyf/85196-follow-up-tests
[NoQA] test: add unit tests for NetSuite credential commands
2 parents d6841fd + ef2ea1d commit 66c8c1d

3 files changed

Lines changed: 171 additions & 2 deletions

File tree

src/libs/actions/connections/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,14 @@ function isConnectionUnverified(policy: OnyxEntry<Policy>, connectionName: Polic
280280
return !(policy?.connections?.[connectionName]?.lastSync?.isConnected ?? true);
281281
}
282282

283+
/**
284+
* Determines whether to use updateNetSuiteTokens (preserves config) or connectPolicyToNetSuite (full init)
285+
* based on the connection's authentication and verification state.
286+
*/
287+
function shouldUseUpdateNetSuiteTokens(policy: OnyxEntry<Policy>): boolean {
288+
return isAuthenticationError(policy, CONST.POLICY.CONNECTIONS.NAME.NETSUITE) && !isConnectionUnverified(policy, CONST.POLICY.CONNECTIONS.NAME.NETSUITE);
289+
}
290+
283291
function setConnectionError(policyID: string, connectionName: PolicyConnectionName, errorMessage?: string) {
284292
Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {
285293
connections: {
@@ -359,4 +367,5 @@ export {
359367
isConnectionInProgress,
360368
hasSynchronizationErrorMessage,
361369
setConnectionError,
370+
shouldUseUpdateNetSuiteTokens,
362371
};

src/pages/workspace/accounting/netsuite/NetSuiteTokenInput/subPages/NetSuiteTokenInputForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import useAutoFocusInput from '@hooks/useAutoFocusInput';
1010
import useLocalize from '@hooks/useLocalize';
1111
import usePolicy from '@hooks/usePolicy';
1212
import useThemeStyles from '@hooks/useThemeStyles';
13-
import {isAuthenticationError, isConnectionUnverified} from '@libs/actions/connections';
13+
import {shouldUseUpdateNetSuiteTokens} from '@libs/actions/connections';
1414
import {connectPolicyToNetSuite, updateNetSuiteTokens} from '@libs/actions/connections/NetSuiteCommands';
1515
import {isMobileSafari} from '@libs/Browser';
1616
import {addErrorMessage} from '@libs/ErrorUtils';
@@ -49,7 +49,7 @@ function NetSuiteTokenInputForm({onNext, policyID}: CustomSubPageTokenInputProps
4949
return;
5050
}
5151

52-
if (isAuthenticationError(policy, CONST.POLICY.CONNECTIONS.NAME.NETSUITE) && !isConnectionUnverified(policy, CONST.POLICY.CONNECTIONS.NAME.NETSUITE)) {
52+
if (shouldUseUpdateNetSuiteTokens(policy)) {
5353
updateNetSuiteTokens(policyID, formValues);
5454
} else {
5555
connectPolicyToNetSuite(policyID, formValues);
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
import Onyx from 'react-native-onyx';
2+
import type {OnyxEntry} from 'react-native-onyx';
3+
import {shouldUseUpdateNetSuiteTokens} from '@libs/actions/connections';
4+
import {connectPolicyToNetSuite, updateNetSuiteTokens} from '@libs/actions/connections/NetSuiteCommands';
5+
// eslint-disable-next-line no-restricted-syntax -- this is required to allow mocking
6+
import * as API from '@libs/API';
7+
import type {WriteCommand} from '@libs/API/types';
8+
import {WRITE_COMMANDS} from '@libs/API/types';
9+
import CONST from '@src/CONST';
10+
import ONYXKEYS from '@src/ONYXKEYS';
11+
import type {Policy} from '@src/types/onyx';
12+
import type {AnyOnyxData} from '@src/types/onyx/Request';
13+
import waitForBatchedUpdates from '../../utils/waitForBatchedUpdates';
14+
15+
jest.mock('@libs/API');
16+
17+
const writeSpy = jest.spyOn(API, 'write');
18+
19+
const MOCK_POLICY_ID = 'MOCK_POLICY_ID';
20+
const MOCK_CREDENTIALS = {
21+
netSuiteAccountID: 'account-123',
22+
netSuiteTokenID: 'token-123',
23+
netSuiteTokenSecret: 'secret-123',
24+
};
25+
26+
function getFirstWriteCall(): {command: WriteCommand; onyxData?: AnyOnyxData} {
27+
const call = writeSpy.mock.calls.at(0);
28+
if (!call) {
29+
throw new Error('API.write was not called');
30+
}
31+
const [command, , onyxData] = call;
32+
return {command, onyxData};
33+
}
34+
35+
function createPolicy(options: {isAuthError?: boolean; verified?: boolean}): OnyxEntry<Policy> {
36+
return {
37+
id: MOCK_POLICY_ID,
38+
connections: {
39+
netsuite: {
40+
verified: options.verified ?? false,
41+
lastSync: {
42+
isAuthenticationError: options.isAuthError ?? false,
43+
},
44+
},
45+
},
46+
} as unknown as Policy;
47+
}
48+
49+
describe('actions/connections/NetSuite', () => {
50+
beforeAll(() => {
51+
Onyx.init({
52+
keys: ONYXKEYS,
53+
});
54+
});
55+
56+
beforeEach(() => {
57+
jest.clearAllMocks();
58+
return Onyx.clear().then(waitForBatchedUpdates);
59+
});
60+
61+
describe('connectPolicyToNetSuite', () => {
62+
it('writes the ConnectPolicyToNetSuite command', () => {
63+
connectPolicyToNetSuite(MOCK_POLICY_ID, MOCK_CREDENTIALS);
64+
65+
const {command} = getFirstWriteCall();
66+
expect(command).toBe(WRITE_COMMANDS.CONNECT_POLICY_TO_NETSUITE);
67+
});
68+
69+
it('passes the policyID and credentials as parameters', () => {
70+
connectPolicyToNetSuite(MOCK_POLICY_ID, MOCK_CREDENTIALS);
71+
72+
const call = writeSpy.mock.calls.at(0);
73+
expect(call).toBeDefined();
74+
const params = call?.[1];
75+
expect(params).toEqual({
76+
policyID: MOCK_POLICY_ID,
77+
...MOCK_CREDENTIALS,
78+
});
79+
});
80+
81+
it('sets optimistic sync progress data', () => {
82+
connectPolicyToNetSuite(MOCK_POLICY_ID, MOCK_CREDENTIALS);
83+
84+
const {onyxData} = getFirstWriteCall();
85+
const optimisticUpdate = onyxData?.optimisticData?.at(0);
86+
87+
expect(optimisticUpdate?.onyxMethod).toBe(Onyx.METHOD.MERGE);
88+
expect(optimisticUpdate?.key).toBe(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${MOCK_POLICY_ID}`);
89+
expect(optimisticUpdate?.value).toEqual(
90+
expect.objectContaining({
91+
stageInProgress: CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.NETSUITE_SYNC_CONNECTION,
92+
connectionName: CONST.POLICY.CONNECTIONS.NAME.NETSUITE,
93+
}),
94+
);
95+
});
96+
});
97+
98+
describe('updateNetSuiteTokens', () => {
99+
it('writes the UpdateNetSuiteTokens command', () => {
100+
updateNetSuiteTokens(MOCK_POLICY_ID, MOCK_CREDENTIALS);
101+
102+
const {command} = getFirstWriteCall();
103+
expect(command).toBe(WRITE_COMMANDS.UPDATE_NETSUITE_TOKENS);
104+
});
105+
106+
it('passes the policyID and credentials as parameters', () => {
107+
updateNetSuiteTokens(MOCK_POLICY_ID, MOCK_CREDENTIALS);
108+
109+
const call = writeSpy.mock.calls.at(0);
110+
expect(call).toBeDefined();
111+
const params = call?.[1];
112+
expect(params).toEqual({
113+
policyID: MOCK_POLICY_ID,
114+
...MOCK_CREDENTIALS,
115+
});
116+
});
117+
118+
it('sets optimistic sync progress data', () => {
119+
updateNetSuiteTokens(MOCK_POLICY_ID, MOCK_CREDENTIALS);
120+
121+
const {onyxData} = getFirstWriteCall();
122+
const optimisticUpdate = onyxData?.optimisticData?.at(0);
123+
124+
expect(optimisticUpdate?.onyxMethod).toBe(Onyx.METHOD.MERGE);
125+
expect(optimisticUpdate?.key).toBe(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${MOCK_POLICY_ID}`);
126+
expect(optimisticUpdate?.value).toEqual(
127+
expect.objectContaining({
128+
stageInProgress: CONST.POLICY.CONNECTIONS.SYNC_STAGE_NAME.NETSUITE_SYNC_CONNECTION,
129+
connectionName: CONST.POLICY.CONNECTIONS.NAME.NETSUITE,
130+
}),
131+
);
132+
});
133+
});
134+
135+
describe('shouldUseUpdateNetSuiteTokens', () => {
136+
it('returns false for unverified connection with auth error (regression case)', () => {
137+
const policy = createPolicy({isAuthError: true, verified: false});
138+
expect(shouldUseUpdateNetSuiteTokens(policy)).toBe(false);
139+
});
140+
141+
it('returns true for verified connection with auth error', () => {
142+
const policy = createPolicy({isAuthError: true, verified: true});
143+
expect(shouldUseUpdateNetSuiteTokens(policy)).toBe(true);
144+
});
145+
146+
it('returns false when there is no auth error', () => {
147+
const policy = createPolicy({isAuthError: false, verified: true});
148+
expect(shouldUseUpdateNetSuiteTokens(policy)).toBe(false);
149+
});
150+
151+
it('returns false when policy is undefined', () => {
152+
expect(shouldUseUpdateNetSuiteTokens(undefined)).toBe(false);
153+
});
154+
155+
it('returns false for unverified connection without auth error', () => {
156+
const policy = createPolicy({isAuthError: false, verified: false});
157+
expect(shouldUseUpdateNetSuiteTokens(policy)).toBe(false);
158+
});
159+
});
160+
});

0 commit comments

Comments
 (0)