Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug Mode - Report and Report Actions #45481

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
f496a17
feat: debug mode for report and report actions
pac-guerreiro Jul 16, 2024
cc067fb
refactor: apply feedback
pac-guerreiro Jul 22, 2024
5b9c97a
refactor: apply feedback
pac-guerreiro Jul 22, 2024
530a504
feat: add hint to user saying that debug data wont be sent to backend
pac-guerreiro Jul 22, 2024
dfb0935
refactor: remove unused import
pac-guerreiro Jul 22, 2024
e5bc479
feat: add debug menu option to report details page
pac-guerreiro Jul 22, 2024
1b4e6f9
fix: debug menu item showing on report profile when debug mode is dis…
pac-guerreiro Jul 22, 2024
96e9e5f
refactor: apply suggestions
pac-guerreiro Jul 24, 2024
f928ba3
fix: modal stack navigators import paths
pac-guerreiro Jul 26, 2024
85b3265
refactor: apply suggestions
pac-guerreiro Jul 26, 2024
c5c703b
refactor: apply suggestions
pac-guerreiro Aug 2, 2024
cc6dff0
refactor: apply suggestions
pac-guerreiro Aug 5, 2024
836518c
fix(debug mode): invalid json property not being validated
pac-guerreiro Aug 5, 2024
b0eac99
fix(debug mode): app crashing without data validation on debug detail…
pac-guerreiro Aug 6, 2024
dcf5e9b
docs(debug mode): add descriptions to debug utils
pac-guerreiro Aug 6, 2024
0f589b8
fix: apply validations to json and onyx data properties
pac-guerreiro Aug 8, 2024
454b80c
chore(debug mode): add unit tests and fix issues
pac-guerreiro Aug 10, 2024
3f7b985
fix: formatting
kubabutkiewicz Aug 14, 2024
ff9b7f6
fix: lint
kubabutkiewicz Aug 14, 2024
9e81fdb
refactor(debug mode): use debug utils in tests
pac-guerreiro Aug 21, 2024
212b279
feat: update inputs with checkboxes and dropdowns for debug page
pasyukevich Aug 28, 2024
ef9bb41
chore: update to use MenuItemWithTopDescription instead of MenuItem
pasyukevich Aug 28, 2024
af50b00
feat: update debug page wth form wrapper
pasyukevich Sep 4, 2024
f71d168
refactor: debug utils and tests
pac-guerreiro Sep 4, 2024
65a5490
chore: Update onSave function signature in DebugDetails component
pasyukevich Sep 4, 2024
f809994
refactor: Update form types to unified, adjust styles, apply reusable…
pasyukevich Sep 4, 2024
efa8aae
refactor: remove unused code
pac-guerreiro Sep 4, 2024
a8ae4e6
Address review comments
fabioh8010 Sep 4, 2024
ae1db62
Fix lint
fabioh8010 Sep 4, 2024
973dcbd
Simplify tests
fabioh8010 Sep 5, 2024
d529ec1
Refactor Selection_List_Action_Type to Debug_Report_Action_Type_List
fabioh8010 Sep 5, 2024
dc14995
Implement Debug actions file and move Onyx calls to it
fabioh8010 Sep 5, 2024
e1fd08b
General improvements on debug components
fabioh8010 Sep 6, 2024
0ee0f29
Use DebugUtils.stringifyJSON
fabioh8010 Sep 6, 2024
55d9692
refactor(debug): add sections to details, create constant and datetim…
pac-guerreiro Sep 9, 2024
9cc2444
fix(debug): multiple UI issues
pac-guerreiro Sep 9, 2024
fd67f61
fix(debug): numpad not working and app crash on mobile native
pac-guerreiro Sep 9, 2024
40115cb
chore: Update button styles in DebugDetails and DebugReportActions
pasyukevich Sep 10, 2024
557a867
Address review comments
fabioh8010 Sep 11, 2024
1b3407e
fix(debug): styling issues
pac-guerreiro Sep 11, 2024
887a9e0
fix(debug): styling issues
pac-guerreiro Sep 11, 2024
985c7ef
refactor(debug): apply suggestions and add translations
pac-guerreiro Sep 12, 2024
09a7468
chore(debug): fix use case for validateNumber
pac-guerreiro Sep 12, 2024
4c9a170
chore(debug): fix linter issues
pac-guerreiro Sep 12, 2024
2c5670d
refactor(debug): apply suggestions
pac-guerreiro Sep 13, 2024
29f285d
fix(debug): missing property validation for reportActionTimestamp
pac-guerreiro Sep 16, 2024
38647dc
fix(debug): broken search in constant picker page
pac-guerreiro Sep 16, 2024
79d057b
fix(debug): multiple validation issues with report actions
pac-guerreiro Sep 17, 2024
734aafb
fix(debug): app crashes by modifying ids
pac-guerreiro Sep 17, 2024
ee000a1
fix(debug): broken search in constant picker
pac-guerreiro Sep 17, 2024
01c43d8
refactor(debug): restore previous behaviour in order to prevent bugs
pac-guerreiro Sep 17, 2024
df08c23
refactor(debug): revert changes to the order of dependencies
pac-guerreiro Sep 17, 2024
506f419
refactor(debug): apply suggestions
pac-guerreiro Sep 17, 2024
ba34ca2
refactor: replace withOnyx usage with useOnyx
pac-guerreiro Sep 17, 2024
014b3d6
chore(debug): resolve rebase conflicts
pac-guerreiro Sep 18, 2024
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
9 changes: 9 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2805,6 +2805,7 @@ const CONST = {
MARK_AS_INCOMPLETE: 'markAsIncomplete',
CANCEL_PAYMENT: 'cancelPayment',
UNAPPROVE: 'unapprove',
DEBUG: 'debug',
},
EDIT_REQUEST_FIELD: {
AMOUNT: 'amount',
Expand Down Expand Up @@ -4140,6 +4141,7 @@ const CONST = {
CARD_AUTHENTICATION_REQUIRED: 'authentication_required',
},
TAB: {
DEBUG_TAB_ID: 'DebugTab',
NEW_CHAT_TAB_ID: 'NewChatTab',
NEW_CHAT: 'chat',
NEW_ROOM: 'room',
Expand Down Expand Up @@ -5754,6 +5756,13 @@ const CONST = {
CATEGORIES_ARTICLE_LINK: 'https://help.expensify.com/articles/expensify-classic/workspaces/Create-categories#import-custom-categories',
TAGS_ARTICLE_LINK: 'https://help.expensify.com/articles/expensify-classic/workspaces/Create-tags#import-a-spreadsheet-1',
},

DEBUG: {
DETAILS: 'details',
JSON: 'json',
REPORT_ACTIONS: 'actions',
REPORT_ACTION_PREVIEW: 'preview',
},
} as const;

type Country = keyof typeof CONST.ALL_COUNTRIES;
Expand Down
9 changes: 9 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,12 @@ const ONYXKEYS = {
RULES_MAX_EXPENSE_AMOUNT_FORM_DRAFT: 'rulesMaxExpenseAmountFormDraft',
RULES_MAX_EXPENSE_AGE_FORM: 'rulesMaxExpenseAgeForm',
RULES_MAX_EXPENSE_AGE_FORM_DRAFT: 'rulesMaxExpenseAgeFormDraft',
DEBUG_REPORT_PAGE_FORM: 'debugReportPageForm',
DEBUG_REPORT_PAGE_FORM_DRAFT: 'debugReportPageFormDraft',
DEBUG_REPORT_ACTION_PAGE_FORM: 'debugReportActionPageForm',
DEBUG_REPORT_ACTION_PAGE_FORM_DRAFT: 'debugReportActionPageFormDraft',
DEBUG_DETAILS_FORM: 'debugDetailsForm',
DEBUG_DETAILS_FORM_DRAFT: 'debugDetailsFormDraft',
},
} as const;

Expand Down Expand Up @@ -786,6 +792,9 @@ type OnyxFormValuesMapping = {
[ONYXKEYS.FORMS.RULES_MAX_EXPENSE_AMOUNT_FORM]: FormTypes.RulesMaxExpenseAmountForm;
[ONYXKEYS.FORMS.RULES_MAX_EXPENSE_AGE_FORM]: FormTypes.RulesMaxExpenseAgeForm;
[ONYXKEYS.FORMS.SEARCH_SAVED_SEARCH_RENAME_FORM]: FormTypes.SearchSavedSearchRenameForm;
[ONYXKEYS.FORMS.DEBUG_REPORT_PAGE_FORM]: FormTypes.DebugReportForm;
[ONYXKEYS.FORMS.DEBUG_REPORT_ACTION_PAGE_FORM]: FormTypes.DebugReportActionForm;
[ONYXKEYS.FORMS.DEBUG_DETAILS_FORM]: FormTypes.DebugReportForm | FormTypes.DebugReportActionForm;
};

type OnyxFormDraftValuesMapping = {
Expand Down
44 changes: 44 additions & 0 deletions src/ROUTES.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1483,6 +1483,50 @@ const ROUTES = {
route: 'settings/workspaces/:policyID/accounting/sage-intacct/advanced/payment-account',
getRoute: (policyID: string) => `settings/workspaces/${policyID}/accounting/sage-intacct/advanced/payment-account` as const,
},
DEBUG_REPORT: {
route: 'debug/report/:reportID',
getRoute: (reportID: string) => `debug/report/${reportID}` as const,
},
DEBUG_REPORT_TAB_DETAILS: {
route: 'debug/report/:reportID/details',
getRoute: (reportID: string) => `debug/report/${reportID}/details` as const,
},
DEBUG_REPORT_TAB_JSON: {
route: 'debug/report/:reportID/json',
getRoute: (reportID: string) => `debug/report/${reportID}/json` as const,
},
DEBUG_REPORT_TAB_ACTIONS: {
route: 'debug/report/:reportID/actions',
getRoute: (reportID: string) => `debug/report/${reportID}/actions` as const,
},
DEBUG_REPORT_ACTION: {
route: 'debug/report/:reportID/actions/:reportActionID',
getRoute: (reportID: string, reportActionID: string) => `debug/report/${reportID}/actions/${reportActionID}` as const,
},
DEBUG_REPORT_ACTION_CREATE: {
route: 'debug/report/:reportID/actions/create',
getRoute: (reportID: string) => `debug/report/${reportID}/actions/create` as const,
},
DEBUG_REPORT_ACTION_TAB_DETAILS: {
route: 'debug/report/:reportID/actions/:reportActionID/details',
getRoute: (reportID: string, reportActionID: string) => `debug/report/${reportID}/actions/${reportActionID}/details` as const,
},
DEBUG_REPORT_ACTION_TAB_JSON: {
route: 'debug/report/:reportID/actions/:reportActionID/json',
getRoute: (reportID: string, reportActionID: string) => `debug/report/${reportID}/actions/${reportActionID}/json` as const,
},
pac-guerreiro marked this conversation as resolved.
Show resolved Hide resolved
DEBUG_REPORT_ACTION_TAB_PREVIEW: {
route: 'debug/report/:reportID/actions/:reportActionID/preview',
getRoute: (reportID: string, reportActionID: string) => `debug/report/${reportID}/actions/${reportActionID}/preview` as const,
},
DETAILS_CONSTANT_PICKER_PAGE: {
route: 'debug/details/constant/:fieldName',
getRoute: (fieldName: string, fieldValue?: string, backTo?: string) => getUrlWithBackToParam(`debug/details/constant/${fieldName}?fieldValue=${fieldValue}`, backTo),
},
DETAILS_DATE_TIME_PICKER_PAGE: {
route: 'debug/details/datetime/:fieldName',
getRoute: (fieldName: string, fieldValue?: string, backTo?: string) => getUrlWithBackToParam(`debug/details/datetime/${fieldName}?fieldValue=${fieldValue}`, backTo),
},
} as const;

/**
Expand Down
8 changes: 8 additions & 0 deletions src/SCREENS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ const SCREENS = {
RESTRICTED_ACTION: 'RestrictedAction',
REPORT_EXPORT: 'Report_Export',
MISSING_PERSONAL_DETAILS: 'MissingPersonalDetails',
DEBUG: 'Debug',
},
ONBOARDING_MODAL: {
ONBOARDING: 'Onboarding',
Expand Down Expand Up @@ -551,6 +552,13 @@ const SCREENS = {
FEATURE_TRAINING_ROOT: 'FeatureTraining_Root',
RESTRICTED_ACTION_ROOT: 'RestrictedAction_Root',
MISSING_PERSONAL_DETAILS_ROOT: 'MissingPersonalDetails_Root',
DEBUG: {
REPORT: 'Debug_Report',
REPORT_ACTION: 'Debug_Report_Action',
REPORT_ACTION_CREATE: 'Debug_Report_Action_Create',
DETAILS_CONSTANT_PICKER_PAGE: 'Debug_Details_Constant_Picker_Page',
DETAILS_DATE_TIME_PICKER_PAGE: 'Debug_Details_Date_Time_Picker_Page',
},
} as const;

type Screen = DeepValueOf<typeof SCREENS>;
Expand Down
10 changes: 10 additions & 0 deletions src/components/AccountSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ function AccountSwitcher() {
const {canUseNewDotCopilot} = usePermissions();
const {shouldUseNarrowLayout} = useResponsiveLayout();
const [account] = useOnyx(ONYXKEYS.ACCOUNT);
const [session] = useOnyx(ONYXKEYS.SESSION);
const [user] = useOnyx(ONYXKEYS.USER);
const buttonRef = useRef<HTMLDivElement>(null);

const [shouldShowDelegatorMenu, setShouldShowDelegatorMenu] = useState(false);
Expand Down Expand Up @@ -166,6 +168,14 @@ function AccountSwitcher() {
>
{Str.removeSMSDomain(currentUserPersonalDetails?.login ?? '')}
</Text>
{!!user?.isDebugModeEnabled && (
<Text
style={[styles.textLabelSupporting, styles.mt1, styles.w100]}
numberOfLines={1}
>
AccountID: {session?.accountID}
</Text>
)}
</View>
</View>
</PressableWithFeedback>
Expand Down
2 changes: 1 addition & 1 deletion src/components/DatePicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type DatePickerProps = {
maxDate?: Date;

/** A function that is passed by FormWrapper */
onInputChange?: (value: Date) => void;
onInputChange?: (value: string) => void;
pac-guerreiro marked this conversation as resolved.
Show resolved Hide resolved

/** A function that is passed by FormWrapper */
onTouched?: () => void;
Expand Down
68 changes: 37 additions & 31 deletions src/components/Form/FormProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ type FormProviderProps<TFormID extends OnyxFormKey = OnyxFormKey> = FormProvider

/** Whether button is disabled */
isSubmitDisabled?: boolean;

/** Whether HTML is allowed in form inputs */
allowHTML?: boolean;
};

function FormProvider(
Expand All @@ -92,6 +95,7 @@ function FormProvider(
draftValues,
onSubmit,
shouldTrimValues = true,
allowHTML = false,
...rest
}: FormProviderProps,
forwardedRef: ForwardedRef<FormRef>,
Expand All @@ -114,40 +118,42 @@ function FormProvider(

const validateErrors: GenericFormInputErrors = validate?.(trimmedStringValues) ?? {};

// Validate the input for html tags. It should supersede any other error
Object.entries(trimmedStringValues).forEach(([inputID, inputValue]) => {
// If the input value is empty OR is non-string, we don't need to validate it for HTML tags
if (!inputValue || typeof inputValue !== 'string') {
return;
}
const foundHtmlTagIndex = inputValue.search(CONST.VALIDATE_FOR_HTML_TAG_REGEX);
const leadingSpaceIndex = inputValue.search(CONST.VALIDATE_FOR_LEADINGSPACES_HTML_TAG_REGEX);

// Return early if there are no HTML characters
if (leadingSpaceIndex === -1 && foundHtmlTagIndex === -1) {
return;
}

const matchedHtmlTags = inputValue.match(CONST.VALIDATE_FOR_HTML_TAG_REGEX);
let isMatch = CONST.WHITELISTED_TAGS.some((regex) => regex.test(inputValue));
// Check for any matches that the original regex (foundHtmlTagIndex) matched
if (matchedHtmlTags) {
// Check if any matched inputs does not match in WHITELISTED_TAGS list and return early if needed.
for (const htmlTag of matchedHtmlTags) {
isMatch = CONST.WHITELISTED_TAGS.some((regex) => regex.test(htmlTag));
if (!isMatch) {
break;
if (!allowHTML) {
// Validate the input for html tags. It should supersede any other error
Object.entries(trimmedStringValues).forEach(([inputID, inputValue]) => {
// If the input value is empty OR is non-string, we don't need to validate it for HTML tags
if (!inputValue || typeof inputValue !== 'string') {
return;
}
const foundHtmlTagIndex = inputValue.search(CONST.VALIDATE_FOR_HTML_TAG_REGEX);
const leadingSpaceIndex = inputValue.search(CONST.VALIDATE_FOR_LEADINGSPACES_HTML_TAG_REGEX);

// Return early if there are no HTML characters
if (leadingSpaceIndex === -1 && foundHtmlTagIndex === -1) {
return;
}

const matchedHtmlTags = inputValue.match(CONST.VALIDATE_FOR_HTML_TAG_REGEX);
let isMatch = CONST.WHITELISTED_TAGS.some((regex) => regex.test(inputValue));
// Check for any matches that the original regex (foundHtmlTagIndex) matched
if (matchedHtmlTags) {
// Check if any matched inputs does not match in WHITELISTED_TAGS list and return early if needed.
for (const htmlTag of matchedHtmlTags) {
isMatch = CONST.WHITELISTED_TAGS.some((regex) => regex.test(htmlTag));
if (!isMatch) {
break;
}
}
}
}

if (isMatch && leadingSpaceIndex === -1) {
return;
}
if (isMatch && leadingSpaceIndex === -1) {
return;
}

// Add a validation error here because it is a string value that contains HTML characters
validateErrors[inputID] = translate('common.error.invalidCharacter');
});
// Add a validation error here because it is a string value that contains HTML characters
validateErrors[inputID] = translate('common.error.invalidCharacter');
});
}

if (typeof validateErrors !== 'object') {
throw new Error('Validate callback must return an empty object or an object with shape {inputID: error}');
Expand All @@ -161,7 +167,7 @@ function FormProvider(

return touchedInputErrors;
},
[shouldTrimValues, formID, validate, errors, translate],
[shouldTrimValues, formID, validate, errors, translate, allowHTML],
);

// When locales change from another session of the same account,
Expand Down
4 changes: 3 additions & 1 deletion src/components/Form/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type StateSelector from '@components/StateSelector';
import type TextInput from '@components/TextInput';
import type TextPicker from '@components/TextPicker';
import type ValuePicker from '@components/ValuePicker';
import type ConstantSelector from '@pages/Debug/ConstantSelector';
import type BusinessTypePicker from '@pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker';
import type DimensionTypeSelector from '@pages/workspace/accounting/intacct/import/DimensionTypeSelector';
import type NetSuiteCustomFieldMappingPicker from '@pages/workspace/accounting/netsuite/import/NetSuiteImportCustomFieldNew/NetSuiteCustomFieldMappingPicker';
Expand Down Expand Up @@ -61,7 +62,8 @@ type ValidInputs =
| typeof NetSuiteCustomFieldMappingPicker
| typeof NetSuiteMenuWithTopDescriptionForm
| typeof CountryPicker
| typeof StatePicker;
| typeof StatePicker
| typeof ConstantSelector;

type ValueTypeKey = 'string' | 'boolean' | 'date' | 'country' | 'reportFields' | 'disabledListValues';
type ValueTypeMap = {
Expand Down
8 changes: 8 additions & 0 deletions src/components/TabSelector/TabSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ type IconAndTitle = {

function getIconAndTitle(route: string, translate: LocaleContextProps['translate']): IconAndTitle {
switch (route) {
case CONST.DEBUG.DETAILS:
return {icon: Expensicons.Info, title: translate('debug.details')};
case CONST.DEBUG.JSON:
return {icon: Expensicons.Eye, title: translate('debug.JSON')};
case CONST.DEBUG.REPORT_ACTIONS:
return {icon: Expensicons.Document, title: translate('debug.reportActions')};
case CONST.DEBUG.REPORT_ACTION_PREVIEW:
return {icon: Expensicons.Document, title: translate('debug.reportActionPreview')};
case CONST.TAB_REQUEST.MANUAL:
return {icon: Expensicons.Pencil, title: translate('tabSelector.manual')};
case CONST.TAB_REQUEST.SCAN:
Expand Down
39 changes: 23 additions & 16 deletions src/components/TestToolMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import type {OnyxEntry} from 'react-native-onyx';
import {withOnyx} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ApiUtils from '@libs/ApiUtils';
Expand All @@ -17,19 +17,23 @@ import TestCrash from './TestCrash';
import TestToolRow from './TestToolRow';
import Text from './Text';

type TestToolMenuOnyxProps = {
/** User object in Onyx */
user: OnyxEntry<UserOnyx>;
};

type TestToolMenuProps = TestToolMenuOnyxProps & {
type TestToolMenuProps = {
/** Network object in Onyx */
network: OnyxEntry<NetworkOnyx>;
};
const USER_DEFAULT: UserOnyx = {shouldUseStagingServer: undefined, isSubscribedToNewsletter: false, validated: false, isFromPublicDomain: false, isUsingExpensifyCard: false};
const USER_DEFAULT: UserOnyx = {
shouldUseStagingServer: undefined,
isSubscribedToNewsletter: false,
validated: false,
isFromPublicDomain: false,
isUsingExpensifyCard: false,
isDebugModeEnabled: false,
};

function TestToolMenu({user = USER_DEFAULT, network}: TestToolMenuProps) {
function TestToolMenu({network}: TestToolMenuProps) {
const [user = USER_DEFAULT] = useOnyx(ONYXKEYS.USER);
const shouldUseStagingServer = user?.shouldUseStagingServer ?? ApiUtils.isUsingStagingApi();
const isDebugModeEnabled = !!user?.isDebugModeEnabled;
const styles = useThemeStyles();
const {translate} = useLocalize();

Expand All @@ -41,6 +45,15 @@ function TestToolMenu({user = USER_DEFAULT, network}: TestToolMenuProps) {
>
{translate('initialSettingsPage.troubleshoot.testingPreferences')}
</Text>
{/* When toggled the app will be put into debug mode. */}
<TestToolRow title={translate('initialSettingsPage.troubleshoot.debugMode')}>
<Switch
accessibilityLabel={translate('initialSettingsPage.troubleshoot.debugMode')}
isOn={isDebugModeEnabled}
onToggle={() => User.setIsDebugModeEnabled(!isDebugModeEnabled)}
/>
</TestToolRow>

{/* Option to switch between staging and default api endpoints.
This enables QA, internal testers and external devs to take advantage of sandbox environments for 3rd party services like Plaid and Onfido.
This toggle is not rendered for internal devs as they make environment changes directly to the .env file. */}
Expand Down Expand Up @@ -97,10 +110,4 @@ function TestToolMenu({user = USER_DEFAULT, network}: TestToolMenuProps) {

TestToolMenu.displayName = 'TestToolMenu';

export default withNetwork()(
withOnyx<TestToolMenuProps, TestToolMenuOnyxProps>({
user: {
key: ONYXKEYS.USER,
},
})(TestToolMenu),
);
export default withNetwork()(TestToolMenu);
Loading
Loading