Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/libs/actions/Policy/Tag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import type {OnyxData} from '@src/types/onyx/Request';

let allPolicyTags: OnyxCollection<PolicyTagLists> = {};
Onyx.connect({

Check warning on line 39 in src/libs/actions/Policy/Tag.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY_TAGS,
waitForCollectionCallback: true,
callback: (value) => {
Expand Down Expand Up @@ -521,8 +521,14 @@
});
}

function clearPolicyTagListErrors(policyID: string, tagListIndex: number) {
const policyTag = PolicyUtils.getTagLists(allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {})?.at(tagListIndex);
type ClearPolicyTagListErrorsProps = {
policyID: string;
tagListIndex: number;
policyTags: OnyxEntry<PolicyTagLists>;
};

function clearPolicyTagListErrors({policyID, tagListIndex, policyTags}: ClearPolicyTagListErrorsProps) {
const policyTag = PolicyUtils.getTagLists(policyTags ?? {})?.at(tagListIndex);

if (!policyTag) {
return;
Expand Down
2 changes: 1 addition & 1 deletion src/pages/workspace/tags/WorkspaceTagsSettingsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function WorkspaceTagsSettingsPage({route}: WorkspaceTagsSettingsPageProps) {
{!isMultiLevelTags && (
<OfflineWithFeedback
errors={policyTags?.[policyTagLists.at(0)?.name ?? '']?.errors}
onClose={() => clearPolicyTagListErrors(policyID, policyTagLists.at(0)?.orderWeight ?? 0)}
onClose={() => clearPolicyTagListErrors({policyID, tagListIndex: policyTagLists.at(0)?.orderWeight ?? 0, policyTags})}
pendingAction={policyTags?.[policyTagLists.at(0)?.name ?? '']?.pendingAction}
errorRowStyles={styles.mh5}
>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/workspace/tags/WorkspaceViewTagsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ function WorkspaceViewTagsPage({route}: WorkspaceViewTagsProps) {
)}
<OfflineWithFeedback
errors={currentPolicyTag.errors}
onClose={() => clearPolicyTagListErrors(policyID, currentPolicyTag.orderWeight)}
onClose={() => clearPolicyTagListErrors({policyID, tagListIndex: currentPolicyTag.orderWeight, policyTags})}
pendingAction={currentPolicyTag.pendingAction}
errorRowStyles={styles.mh5}
>
Expand Down
194 changes: 194 additions & 0 deletions tests/actions/PolicyTagTest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
buildOptimisticPolicyRecentlyUsedTags,
clearPolicyTagErrors,
clearPolicyTagListErrorField,
clearPolicyTagListErrors,
createPolicyTag,
deletePolicyTags,
enablePolicyTags,
Expand Down Expand Up @@ -902,6 +903,199 @@ describe('actions/Policy', () => {
});
});

describe('ClearPolicyTagListErrors', () => {
it('should clear errors for a tag list', async () => {
// Given a policy with a tag list that has errors
const fakePolicy = createRandomPolicy(0);
const tagListName = 'Test tag list';
const fakePolicyTags = createRandomPolicyTags(tagListName, 2);

fakePolicyTags[tagListName] = {
...fakePolicyTags[tagListName],
errors: {field1: 'Error on tag list'},
};

await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`, fakePolicyTags);

// When clearing the errors from the tag list
clearPolicyTagListErrors({policyID: fakePolicy.id, tagListIndex: 0, policyTags: fakePolicyTags});
await waitForBatchedUpdates();

let updatedPolicyTags: PolicyTagLists | undefined;
await TestHelper.getOnyxData({
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`,
callback: (val) => (updatedPolicyTags = val),
});

// Then the errors should be cleared while other properties remain unchanged
expect(updatedPolicyTags?.[tagListName]).toBeDefined();
expect(updatedPolicyTags?.[tagListName]?.errors).toBeUndefined();
expect(updatedPolicyTags?.[tagListName]?.name).toBe(tagListName);
expect(updatedPolicyTags?.[tagListName]?.orderWeight).toBe(0);
expect(Object.keys(updatedPolicyTags?.[tagListName]?.tags ?? {}).length).toBe(2);
});

it('should not modify Onyx data when tag list does not exist at given index', async () => {
// Given a policy with a tag list
const fakePolicy = createRandomPolicy(0);
const tagListName = 'Test tag list';
const fakePolicyTags = createRandomPolicyTags(tagListName, 2);

await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`, fakePolicyTags);

// When attempting to clear errors for a non-existent tag list using an invalid index
clearPolicyTagListErrors({policyID: fakePolicy.id, tagListIndex: 99, policyTags: fakePolicyTags});
await waitForBatchedUpdates();

let updatedPolicyTags: PolicyTagLists | undefined;
await TestHelper.getOnyxData({
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`,
callback: (val) => (updatedPolicyTags = val),
});

// Then the policy tags should remain unchanged because the index is invalid
expect(updatedPolicyTags).toEqual(fakePolicyTags);
});

it('should not modify Onyx data when tag list name is empty', async () => {
// Given a policy with a tag list that has an empty name
const fakePolicy = createRandomPolicy(0);
const tagListName = 'Test tag list';
const fakePolicyTags = createRandomPolicyTags(tagListName, 2);

fakePolicyTags[tagListName] = {
...fakePolicyTags[tagListName],
name: '',
};

await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`, fakePolicyTags);

// When attempting to clear errors for the tag list with empty name
clearPolicyTagListErrors({policyID: fakePolicy.id, tagListIndex: 0, policyTags: fakePolicyTags});
await waitForBatchedUpdates();

let updatedPolicyTags: PolicyTagLists | undefined;
await TestHelper.getOnyxData({
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`,
callback: (val) => (updatedPolicyTags = val),
});

// Then the policy tags should remain unchanged because the tag list name is empty
expect(updatedPolicyTags).toEqual(fakePolicyTags);
});

it('should clear multiple errors from a tag list', async () => {
// Given a policy with a tag list that has multiple errors
const fakePolicy = createRandomPolicy(0);
const tagListName = 'Test tag list';
const fakePolicyTags = createRandomPolicyTags(tagListName, 3);

fakePolicyTags[tagListName] = {
...fakePolicyTags[tagListName],
errors: {
field1: 'Error 1',
field2: 'Error 2',
field3: 'Error 3',
},
};

await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`, fakePolicyTags);

// When clearing errors from the tag list
clearPolicyTagListErrors({policyID: fakePolicy.id, tagListIndex: 0, policyTags: fakePolicyTags});
await waitForBatchedUpdates();

let updatedPolicyTags: PolicyTagLists | undefined;
await TestHelper.getOnyxData({
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`,
callback: (val) => (updatedPolicyTags = val),
});

// Then all errors should be cleared from the tag list
expect(updatedPolicyTags?.[tagListName]?.errors).toBeUndefined();
});

it('should handle multiple tag lists correctly', async () => {
// Given a policy with multiple tag lists that both have errors
const fakePolicy = createRandomPolicy(0);
const tagListName1 = 'Tag list 1';
const tagListName2 = 'Tag list 2';

const fakePolicyTags: PolicyTagLists = {
[tagListName1]: {
name: tagListName1,
orderWeight: 0,
required: false,
tags: {
tag1: {name: 'tag1', enabled: true},
},
errors: {field: 'Error on list 1'},
},
[tagListName2]: {
name: tagListName2,
orderWeight: 1,
required: false,
tags: {
tag2: {name: 'tag2', enabled: true},
},
errors: {field: 'Error on list 2'},
},
};

await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`, fakePolicyTags);

// When clearing errors only for the second tag list
clearPolicyTagListErrors({policyID: fakePolicy.id, tagListIndex: 1, policyTags: fakePolicyTags});
await waitForBatchedUpdates();

let updatedPolicyTags: PolicyTagLists | undefined;
await TestHelper.getOnyxData({
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`,
callback: (val) => (updatedPolicyTags = val),
});

// Then only the second list should have errors cleared while the first list keeps its errors
expect(updatedPolicyTags?.[tagListName1]?.errors).toEqual({field: 'Error on list 1'});
expect(updatedPolicyTags?.[tagListName2]?.errors).toBeUndefined();
});

it('should work with data from useOnyx hook', async () => {
// Given a policy with a tag list that has errors and is accessed via useOnyx hook
const fakePolicy = createRandomPolicy(0);
const tagListName = 'Test tag list';
const fakePolicyTags = createRandomPolicyTags(tagListName, 2);

fakePolicyTags[tagListName] = {
...fakePolicyTags[tagListName],
errors: {field: 'Test error'},
};

await Onyx.set(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`, fakePolicyTags);

const {result} = renderHook(() => useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`));

await waitFor(() => {
expect(result.current[0]).toBeDefined();
});

// When clearing errors using data from the useOnyx hook
clearPolicyTagListErrors({policyID: fakePolicy.id, tagListIndex: 0, policyTags: result.current[0]});
await waitForBatchedUpdates();

let updatedPolicyTags: PolicyTagLists | undefined;
await TestHelper.getOnyxData({
key: `${ONYXKEYS.COLLECTION.POLICY_TAGS}${fakePolicy.id}`,
callback: (val) => (updatedPolicyTags = val),
});

// Then the errors should be cleared and other properties should remain unchanged
expect(updatedPolicyTags?.[tagListName]).toBeDefined();
expect(updatedPolicyTags?.[tagListName]?.errors).toBeUndefined();
expect(updatedPolicyTags?.[tagListName]?.name).toBe(tagListName);
expect(updatedPolicyTags?.[tagListName]?.orderWeight).toBe(0);
});
});

describe('ClearPolicyTagErrors', () => {
it('should clear errors for a tag', async () => {
const fakePolicy = createRandomPolicy(0);
Expand Down
Loading