Skip to content

Thread beta overrides into the remaining util call sites - #100061

Merged
mountiny merged 44 commits into
Expensify:mainfrom
arekm213:arekm213/feat/98409-beta-overrides-utils
Sep 17, 2026
Merged

mountiny merged 44 commits into
Expensify:mainfrom
arekm213:arekm213/feat/98409-beta-overrides-utils

Conversation

@arekm213

@arekm213 arekm213 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Explanation of Change

Follow up to #99444, which added local beta overrides but left three betas resolving from the raw betas array, so an override never reached them.

The three remaining betas are now override aware:

  • asapSubmit and defaultRooms: the betas parameter is replaced by the resolved boolean (isASAPSubmitBetaEnabled, isDefaultRoomsBetaEnabled), passed down from usePermissions at the hook or component boundary. This is the pattern the codebase already uses at over 130 call sites covering more than twenty other betas: resolve once where usePermissions is available, then pass the answer down. Since usePermissions merges betas, configuration and overrides, nothing downstream can drift.
  • vendorMatching: ViolationsUtils used to resolve this beta itself from module level Onyx subscriptions. Those are gone, including the BETAS one that predates this PR. getViolationsOnyxData takes the resolved value as a parameter and the components pass it down, the same way the other two betas work here. The value is boolean | undefined rather than a plain boolean, because the code it feeds has to tell "the beta is off" apart from "the account betas have not loaded yet" and must leave existing violations alone in the second case. usePermissions exposes isBetaEnabledOrUnknown for the few call sites that need that distinction, and everything else keeps using isBetaEnabled.
  • TransactionInlineEdit took the same route until main made it pure. It now receives isASAPSubmitBetaEnabled from useTransactionInlineEdit, which resolves it through usePermissions, so the file holds no Onyx subscriptions of its own.

Because the boolean replaces the array, the betas plumbing that existed only to answer these checks is now dead and has been removed.

Permissions.isBetaEnabled also makes betaConfiguration and betaOverrides required rather than optional. Only two call sites remain in src, usePermissions and the beta overrides page, and the page passes undefined for the overrides explicitly, with a comment, because it deliberately shows what the account alone resolves to. A required parameter is what stops the next util from quietly resolving a beta with two arguments.

Unifying how these betas resolve

Permissions.isBetaEnabled only applies the explicitOnly and exclusion rules when betaConfiguration is supplied. A two argument call therefore reports an explicit-only beta as enabled, and an exclusion beta as enabled, for every account holding the all beta. Accounts without all are unaffected: the branch is never reached and both forms reduce to betas.includes(beta).

Before this PR, asapSubmit (an explicit-only beta) was resolved without the configuration in ReportUtils, Search and Policy, while every UI check resolved it with the configuration through usePermissions. The two disagreed on all beta accounts. Routing these call sites through usePermissions makes them agree, which means that on an all beta account without asapSubmit granted explicitly, these paths now resolve it to false the way the rest of the app already did.

This is the same change of behaviour that #100851 reported for preventSpotnanaTravel (an exclusion beta) after #99444, and that issue was closed as intended behaviour. The durable fix for an account that wants the feature is to grant the beta explicitly rather than rely on all. The analysis on that issue also named the call sites still carrying the two argument form (ReportUtils.ts, Search.ts, all ASAP_SUBMIT), which are exactly the ones this PR converts.

defaultRooms moves the same way. canSeeDefaultRoom used isBetaEnabled(DEFAULT_ROOMS, betas ?? []) and now takes the resolved boolean, so if the backend lists defaultRooms under explicitOnly or exclusion, an all beta account without it granted explicitly loses non partner-managed domain rooms from the LHN, Search and the chat finder. Admin and announce rooms are unaffected, canSeeDefaultRoom returns early for them. This one is the most visible of the three, so it is worth a look during QA even though the mechanism is identical.

ViolationsUtils was resolving vendorMatching without the configuration too, so on an all beta account it could write an inactive vendor violation for a feature the user cannot see. It now receives the value already resolved through usePermissions, so it picks up both the configuration and the overrides.

Behaviour worth calling out beyond "the utils see overrides"

A pre-existing swapped argument pair is fixed. On main, AttachmentPickerWithMenuItems calls createNewReport(personalDetails, isASAPSubmitBetaEnabled, hasViolations, ...) while the signature is (ownerPersonalDetails, hasViolationsParam, isASAPSubmitBetaEnabled, ...). Both are booleans so TypeScript never caught it, and the ten other call sites are correct. Today this only corrupts the next step, because the optimistic report state and status come from the raw betas array further down. This PR moves that resolution to the boolean, which would have made the swap corrupt the state and status as well, so the arguments are put in the right order here rather than left for a separate fix.

setWorkspaceApprovalMode keeps its loading guard. On main the guard reads additionalData?.transactionViolations != null && additionalData?.betas != null && additionalData?.personalDetailsList. The betas null check doubled as a check that Onyx had loaded them, because the callers read the array with useOnyx and passed undefined until it arrived. That is preserved: the field is boolean | undefined, the callers resolve it with isBetaEnabledOrUnknown, and the conjunct is now additionalData?.isASAPSubmitBetaEnabled !== undefined. The optimistic next step update is still skipped while the beta is unknown rather than running with it resolved to off.

A pinned override is ignored for one page's UI gating while the betas load. Permissions.isBetaEnabled checks the overrides first and returns the pinned value before it looks at betas at all. isBetaEnabledOrUnknown short circuits on betas === undefined and returns undefined without consulting them. WorkspaceMoreFeaturesPage now reads the beta once through the second one and coerces it for its two UI checks, so between app start and the BETAS response a pinned vendorMatching reads as off there, where before it read as pinned.

This is left as is deliberately. The scope is dev and staging only, since overrides never apply in production, it lasts only until BETAS arrives, and the row it gates also needs an accounting connection. Making the helper consult the overrides before the short circuit would fix it, but that same helper feeds the violation builders, where returning undefined is exactly what stops an existing server sent INACTIVE_VENDOR from being stripped while the betas are unknown. A pin set to off would start stripping it again, which is the failure this whole design exists to prevent. Honouring a pin a few hundred milliseconds earlier on one settings page is not worth that.

Two call sites keep a hardcoded false on purpose. InSelector and UnreadIndicatorUpdater previously passed betas: undefined and betas: [], which resolved to false for everyone, so the literal keeps their behaviour identical. Passing the real value there would change what the Search filter and the unread indicator show, which is unrelated to overrides and belongs in its own PR.

Follow up: the remaining betas plumbing is dead

After this PR, betas is read in exactly two places: usePermissions and the beta overrides page. Everything else that still carries betas forwards it into one of four parameters that no function body ever reads:

  • OpenReportActionParams.betas in src/libs/actions/Report/index.ts
  • getMoneyRequestOptions in src/libs/ReportUtils.ts
  • completePaymentOnboarding in src/libs/actions/IOU/PayMoneyRequest.ts
  • BuildPolicyDataOptions.betas in src/libs/actions/Policy/Policy.ts

All four were already dead on main, so this is not something this PR introduced. What changed is that they used to sit next to live betas chains and were impossible to tell apart from them. That residue is around 138 files and roughly 100 useOnyx(ONYXKEYS.BETAS) subscriptions that exist only to deliver a value nobody reads. Removing it belongs in a separate PR, kept out of this one so the diff here stays reviewable.

Fixed Issues

$ #98409
PROPOSAL:

Tests

  1. Sign in and open Beta overrides (Cmd/Ctrl+D, or Settings > Troubleshoot on a non production build).
  2. Toggle defaultRooms off and verify a non partner-managed domain room disappears from the LHN without a reload, then toggle it on and verify it comes back. Before this PR the LHN ignored the override entirely. Admin and announce rooms are not gated by this beta, canSeeDefaultRoom returns early for them, so they stay visible either way. Without a domain room this one is only covered by the canSeeDefaultRoom cases in ReportUtilsTest.
  3. This step needs two preconditions, and it cannot be demonstrated without them. First, under Workspace settings > Workflows set Submit frequency to Instantly: on any other frequency getExpenseReportStateAndStatus falls through to the same OPEN / OPEN whether the beta is pinned on or off. Second, go offline before creating the expense. asapSubmit is gated on the backend too, and the backend does not know about a device local override, so online the write command's response replaces the optimistic state a moment later and the report lands on Outstanding either way. Offline the optimistic value survives long enough to read. With both in place, toggle asapSubmit on, create an expense, and verify the report stays in Draft. Toggle the beta off, create an expense on a fresh report rather than adding to the existing draft, and verify that one is Submitted immediately instead. To see the next step differ as well, make one of the expenses break a rule so it carries a violation, since both next step branches are gated on a violation plus instant submit.
  4. With asapSubmit still pinned, approve an expense from Search and verify the pinned value applies there too. Only the pay path honoured it before.
  5. With asapSubmit still pinned, turn Approvals off under Workspace settings > Workflows and verify the next steps on submitted reports match the pinned value.
  6. Press Reset all overrides and verify every beta returns to the value the account has on the backend.
  7. Sign in on an account with no overrides set and verify expense creation, submitting, approving and paying behave exactly as they do on main.

The vendorMatching override needs a QuickBooks Online or Xero workspace with a synced vendor list to demonstrate by hand, so it is covered by unit tests instead: tests/unit/ViolationUtilsTest.ts asserts that pinning the beta on adds the inactive supplier violation when the account does not have the beta, and that pinning it off suppresses the violation when the account does have it.

  • Verify that no errors appear in the JS console

Offline tests

Overrides are stored locally and only affect frontend checks, so for steps 1, 2 and 4 to 7 the behaviour is identical offline.

Step 3 is the exception and has to be run offline. asapSubmit is enforced on the backend as well, and the backend has no knowledge of a device local override, so online the server's own state, status and next step replace the optimistic ones the client built. Offline there is no response to overwrite them, which is the only way to observe the pinned value. The page itself says as much: "Some betas are gated on the backend too, so those can still fail at the request level."

QA Steps

Overrides are hidden in production, so QA is a regression pass rather than a feature test: create, submit, approve and pay expenses, open reports from the LHN, and use the in: filter in Search. Everything should behave exactly as it does today.

One thing to watch on internal accounts holding the all beta. If the backend lists a beta under explicitOnly or exclusion, that beta now resolves to false in the converted paths unless the account is granted it explicitly, which matches how the rest of the app already behaved. That covers asapSubmit in expense creation, Search approve and the workspace approval mode, vendorMatching in the violation builders, and defaultRooms in the LHN, Search and the chat finder. defaultRooms is the visible one: check that non partner-managed domain rooms still appear for an all beta account. See #100851 for the same change on preventSpotnanaTravel.

  • Verify that no errors appear in the JS console

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

No UI changes in this PR, the beta overrides screen itself is in #99444.

@melvin-bot

melvin-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown

Hey, I noticed you changed src/languages/en.ts in a PR from a fork. For security reasons, translations are not generated automatically for PRs from forks.

If you want to automatically generate translations for other locales, an Expensify employee will have to:

  1. Look at the code and make sure there are no malicious changes.
  2. Run the Generate static translations GitHub workflow. If you have write access and the K2 extension, you can simply click: [this button]

Alternatively, if you are an external contributor, you can run the translation script locally with your own OpenAI API key. To learn more, try running:

npx bun ./scripts/generateTranslations.ts --help

Typically, you'd want to translate only what you changed by running npx bun ./scripts/generateTranslations.ts --compare-ref main

@arekm213

arekm213 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@arekm213

arekm213 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

@MelvinBot review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 75beffc515

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +27 to +31
function BetaOverridesPage() {
const styles = useThemeStyles();
const {translate} = useLocalize();
const {isBetaEnabled} = usePermissions();
const [betaOverrides] = useOnyx(ONYXKEYS.BETA_OVERRIDES);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Block the beta override route in production

When an authenticated production user opens /beta-overrides directly, the linking config routes here even though only the Test Tool Menu entry is guarded by !isProduction. This page has no environment check, so it exposes all test switches; because usePermissions ignores overrides in production, toggling a switch writes a hidden override but leaves the effective switch state unchanged. Gate the page or route itself so the production-only restriction cannot be bypassed by a deep link.

Useful? React with 👍 / 👎.

@MelvinBot

Copy link
Copy Markdown
Contributor

Review: 2 blocking test failures, both caused by this PR

The refactor itself is sound — I verified the createNewReport argument-swap fix against the signature and all 11 call sites, and confirmed overrides can't leak into production because Permissions.ts:31 gates on the environment before reading betaOverrides. Two CI failures need fixing first.

🔴 1. Onyx export keys are out of alphabetical order

src/libs/ExportOnyxState/common.ts:172-174 lists BETA_OVERRIDES before BETA_CONFIGURATION. That fails ExportOnyxStateTest › safeOnyxKeys should list its keys alphabetically. Swap the two lines.

🔴 2. useSearchSelector now needs contexts the test mocks away

useSearchSelector/base.ts:199 swapped useOnyx(ONYXKEYS.BETAS) for usePermissions(), which reads BetasContext / BetaConfigurationContext / BetaOverridesContext. But useSearchSelectorTest.tsx:53 replaces the whole @components/OnyxListItemProvider module with just usePersonalDetails, so those contexts are undefined and all 20 tests in the file crash with TypeError: Cannot read properties of undefined (reading '$$typeof').

Fix by spreading jest.requireActual('@components/OnyxListItemProvider') into the mock, or by mocking @hooks/usePermissions directly.

🟡 3. Two test files lost ASAP-submit coverage in the collapse

Where a test passed both isASAPSubmitBetaEnabled: false and betas: [CONST.BETAS.ALL], those were not the same value — the array resolved to true inside shouldCreateNewMoneyRequestReport and getExpenseReportStateAndStatus. Collapsing them to a single false flips which branch the test exercises, silently and without any assertion change:

  • RequestMoneyTest.ts:225 and 12 other requestMoney calls — every one now runs with the beta off, so the optimistic-report state/status path for ASAP submit is no longer covered anywhere in that file.
  • PerDiemTest.ts:337 vs :355 now pass true to getPerDiemExpensePolicyID and false to getPerDiemExpenseInformation in the same test. That is exactly the drift the doc comment on getPerDiemExpensePolicyID tells you to avoid, and it makes earlyPolicyID resolve against a different branch than the information builder.

Please pick the value each test meant, keep the two per-diem calls in sync, and add at least one isASAPSubmitBetaEnabled: true case back.

🟢 4. Nit: dead parameter

betas is still declared on GetApproveActionCallbackParams and still passed by handleActionButtonPress, but getApproveActionCallback no longer reads it. Drop it.

What I checked and found fine
  • isBetaEnabled in dependency arrays. usePermissions memoizes on [betas, betaConfiguration, betaOverrides, isProduction], so swapping betasisBetaEnabled in the useMemo/useCallback deps of SearchAutocompleteList, useSidebarOrderedReports, AmountSubmitDataSync, WorkflowsApprovalsTab and useSearchBulkActions keeps the same invalidation behavior. No extra renders.
  • The argument-swap fix. createNewReport is (ownerPersonalDetails, hasViolationsParam, isASAPSubmitBetaEnabled, policy, betas, …). AttachmentPickerWithMenuItems was the only wrong call site; the other ten already matched. Both of its calls are correct now.
  • The hardcoded false at two call sites. InSelector passed betas: undefined and UnreadIndicatorUpdater passed betas: []; both resolved to false through canSeeDefaultRoom, so the literal is behavior-identical. Agreed these belong in their own PR.
  • The two deliberate behavior changes (applying betaConfiguration in ViolationsUtils and TransactionInlineEdit) only affect accounts on the all beta, and are clearly called out in the description. Worth a second opinion from someone who knows whether vendorMatching is listed under explicitOnly/exclusion, since that decides whether anything actually changes for real users.
  • Production safety. Overrides are read before the betas array in isBetaEnabled, but only when !isProductionEnvironment, so the new Onyx.connectWithoutView reads of BETA_OVERRIDES in ViolationsUtils and TransactionInlineEdit cannot affect production.
  • The third red CI job is not yours. CloudflareSessionTest › propagates an exchange failure fails with ReferenceError: Request is not defined from the expo fetch polyfill — unrelated to these changes.

One process note: this stacks on #99444, which is still open, so it can't merge until that one does.


view run

@codecov

codecov Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.

Files with missing lines Coverage Δ
...ksOnlineFlow/BaseConnectToQuickbooksOnlineFlow.tsx 97.56% <100.00%> (+0.12%) ⬆️
...eaderActions/MoneyReportHeaderSecondaryActions.tsx 0.65% <ø> (ø)
...yReportHeaderPrimaryAction/SubmitPrimaryAction.tsx 1.23% <ø> (+0.01%) ⬆️
...neyReportHeaderPrimaryAction/useConfirmApproval.ts 0.00% <ø> (ø)
...nents/Navigation/QuickCreationActionsBar/index.tsx 92.04% <ø> (-0.09%) ⬇️
...nents/ReportActionItem/MoneyRequestReceiptView.tsx 72.09% <100.00%> (+0.21%) ⬆️
...Item/MoneyRequestReportPreview/PayActionButton.tsx 96.42% <100.00%> (+0.06%) ⬆️
...m/MoneyRequestReportPreview/SubmitActionButton.tsx 98.03% <ø> (-0.04%) ⬇️
...uestReportPreview/useConfirmApproveReportAction.ts 100.00% <ø> (ø)
...c/components/ReportActionItem/MoneyRequestView.tsx 82.11% <100.00%> (+0.04%) ⬆️
... and 127 more
... and 25 files with indirect coverage changes

@arekm213

arekm213 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@arekm213

arekm213 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

@MelvinBot review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: 7981e80083

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@MelvinBot

Copy link
Copy Markdown
Contributor

Re-review of 7981e80: the two blockers are fixed, the new test is wrong

Both failures I flagged are gone — ExportOnyxStateTest passes with the key order swapped, and all 20 useSearchSelectorTest tests pass now that the mock spreads jest.requireActual. The dead betas param is gone too, and the coverage restoration in RequestMoneyTest/PerDiemTest matches what main actually exercised. One new test you added is asserting the wrong value.

🔴 The new asapSubmit state and status test fails on a randomized fixture

ReportUtilsTest.ts:19785 expects STATE_NUM.SUBMITTED (1) but gets OPEN (0), so test (job 2) is still red.

The fixture at ReportUtilsTest.ts:19757 pins autoReporting and autoReportingFrequency, but inherits approvalMode from createRandomPolicy, which picks it at random from Object.values(CONST.POLICY.APPROVAL_MODE). For seed 201 it lands on OPTIONAL, which makes isSubmitAndClose true. buildOptimisticEmptyReport passes isEmptyOptimisticReport: true, so the guard at ReportUtils.ts:7334isInstantSubmitEnabledLocal && !(isSubmitAndCloseLocal && isEmptyOptimisticReport) — is false, and the function correctly returns OPEN.

The production code is right; the fixture just doesn't pin the field the assertion depends on. Add approvalMode: CONST.POLICY.APPROVAL_MODE.BASIC to instantSubmitPolicy and it will reach the SUBMITTED branch.

🟢 Nit: leaked mock state

BetaOverridesPageTest.tsx:116 sets mockIsProduction = true and never resets it. It works today only because that it is last in the file. An afterEach(() => { mockIsProduction = false; }) makes it order-independent.

Verified fixed, plus notes on the production gate
  • ExportOnyxState/common.tsBETA_CONFIGURATION now precedes BETA_OVERRIDES; test (job 5) is green.
  • useSearchSelectorTest.tsx — spreading jest.requireActual<typeof OnyxListItemProvider> restores the three beta contexts, so usePermissions() resolves and all 20 tests pass.
  • Dead parambetas removed from both GetApproveActionCallbackParams and the handleActionButtonPress call site.
  • PerDiemTest.tsgetPerDiemExpensePolicyID and getPerDiemExpenseInformation now both receive true, so the two stay in sync as the doc comment requires.
  • RequestMoneyTest.ts — 32 call sites flipped to true, which restores the branch betas: [CONST.BETAS.ALL] used to select on main. The buildOptimisticEmptyReport beta-enabled case is now covered directly too.
  • The new production gate on BetaOverridesPage is reasonable defence-in-depth, though not strictly required: the authoritative guard is already synchronous in Permissions.ts:31, seeded from CONFIG.ENVIRONMENT before the async isProduction() resolves. Note useEnvironment reads from EnvironmentContextProvider, which populates asynchronously, so on a production build the switches could render for a frame before FullPageNotFoundView takes over. Harmless — the toggles can't affect any beta check — but worth knowing if you'd rather gate the route registration instead.
  • CloudflareSessionTest › propagates an exchange failure is still red in test (job 3) with the same ReferenceError: Request is not defined from the expo fetch polyfill. Nothing in this PR touches that path.

view run

@arekm213

arekm213 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

@MelvinBot review

@MelvinBot

Copy link
Copy Markdown
Contributor

Re-review of 0aed5c7: everything I flagged is fixed — LGTM

Both points from the last round are addressed and CI agrees: test (job 2) and test (job 5) are now green, along with typecheck, spellcheck, React Compiler Compliance and every other jest shard.

  • The randomized fixture is pinned. ReportUtilsTest.ts:19763 now sets approvalMode: BASIC, so isSubmitAndClose is deterministically false and the beta-disabled case reaches the SUBMITTED branch at ReportUtils.ts:7334 regardless of the seed. The explanatory comment is a nice touch — that trap will catch someone else otherwise.
  • The mock no longer leaks. BetaOverridesPageTest.tsx:70-72 resets mockIsProduction in afterEach, so the production-gate test no longer depends on being last in the file.

No new findings. The only red jest job left is CloudflareSessionTest › propagates an exchange failure, failing with ReferenceError: Request is not defined out of the expo fetch polyfill — that file is not in this PR's diff and nothing here touches that path.

Still blocked on #99444 landing first, since this uses the betaOverrides parameter it adds.


view run

@JakubKorytko JakubKorytko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking, a handful of small ones but I could miss something because of the size 😅 tried my best to skip commenting stuff already added by #99444

Comment thread src/libs/actions/Report/index.ts
Comment thread src/libs/actions/Policy/Policy.ts Outdated
Comment thread src/libs/Violations/ViolationsUtils.ts Outdated
Comment thread src/libs/actions/TransactionInlineEdit.ts Outdated
Comment thread src/components/Search/FilterComponents/InSelector.tsx
Comment thread src/libs/UnreadIndicatorUpdater/index.ts
Comment thread src/components/Search/SearchList/ListItem/ActionCell/PayActionCell.tsx Outdated
Comment thread tests/unit/ViolationUtilsTest.ts
@huult

huult commented Sep 17, 2026

Copy link
Copy Markdown
Contributor
Screen.Recording.2026-09-17.at.21.51.21.mov

@arekm213 Is this expected behavior or a bug? I think it’s a bug because when we press Back, we should return to the test tool instead of closing it.

@arekm213

arekm213 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

Screen.Recording.2026-09-17.at.21.51.21.mov
@arekm213 Is this expected behavior or a bug? I think it’s a bug because when we press Back, we should return to the test tool instead of closing it.

@huult its not a bug, i think some other flow in same menu does the same, or we at least agreed upon the behaviour being like this in original PR where we introduced this feature, i will update this comment in a moment with solid informations

edit: you can check out biometrics in test tool, it behaves the same way

@arekm213

arekm213 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

Screen.Recording.2026-09-17.at.21.51.21.mov
@arekm213 Is this expected behavior or a bug? I think it’s a bug because when we press Back, we should return to the test tool instead of closing it.

@huult this came up in original PR - the first version was a modal rendered inside the Test Tools menu exactly so the menu would stay underneath. I flagged the RHP tradeoff here (#99444 (comment)) and design chose the RHP anyway here (#99444 (comment)): "the mobile issue of the troubleshooting menu being closed when you leave the RHP is worthwhile tradeoff for this".

@huult huult left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@melvin-bot
melvin-bot Bot requested a review from mountiny September 17, 2026 17:07

@mountiny mountiny left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😰

@mountiny
mountiny merged commit 06acc2d into Expensify:main Sep 17, 2026
35 of 42 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

🚧 mountiny has triggered a test Expensify/App build. You can view the workflow run here.

@OSBotify

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/mountiny in version: 9.4.82-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 cancelled 🔪
🍎 iOS 🍎 cancelled 🔪

@MelvinBot

Copy link
Copy Markdown
Contributor

No help site changes are required for this PR. I did not create a draft docs PR.

This is an internal refactor of how three betas (asapSubmit, defaultRooms, vendorMatching) are resolved. Nothing it changes is visible to a customer on a production build, so there is nothing in docs/articles to update.

Why I concluded no docs changes are needed

1. The feature this PR extends is hidden in production. Beta overrides only exist on non-production builds — src/pages/settings/Troubleshoot/DynamicBetaOverridesPage.tsx:42-72 renders a FullPageNotFoundView when the environment is production. A help site article can only document what a customer can reach, so the override page and the Troubleshoot entry point are out of scope for HelpDot by design.

2. No UI, copy, or navigation changed. Of the 211 changed files, all are under src/ or tests/ — no src/languages/*.ts and no new screens or labels. Your own PR body says it: "No UI changes in this PR, the beta overrides screen itself is in Add local beta overrides for development." Docs updates are keyed to feature names, tab names, settings labels, and button labels changing; none did here.

3. The one real behavior change does not reach customers. Routing asapSubmit, defaultRooms, and vendorMatching through usePermissions means explicitOnly and exclusion rules now apply in paths that previously skipped them. As your PR body explains, that only differs for accounts holding the all beta — internal Expensify accounts. Customer accounts resolve the same before and after, since the branch is never reached without all.

4. The features these betas gate are already documented, and their documented behavior is unchanged. I checked the articles that would have been affected if customer behavior had shifted:

Each describes the customer-facing feature, not the beta gate. None of them states or implies anything this PR alters.

Per docs/HELP_AUTHORING_GUIDELINES.md, help articles cover what customers can see and do. Writing one here would mean documenting a developer tool that returns a not-found page for every customer.

If you disagree — for example if defaultRooms or asapSubmit is about to roll out more broadly and you want the customer-facing behavior written up ahead of that — reply with @MelvinBot and the article you want, and I'll open the draft PR with the HelpDot label and assign it to you.

@arekm213, please confirm you agree that no help site changes are required. There is no linked help site PR to mark Ready for review, since I did not create one.


view run

@arekm213

Copy link
Copy Markdown
Contributor Author

I agree that no help site changes are required

@kavimuru

kavimuru commented Sep 17, 2026

Copy link
Copy Markdown

@arekm213 please help with QA steps for Applause to perform. Could you provide some detailed steps?

@arekm213

Copy link
Copy Markdown
Contributor Author

@kavimuru sure, here is a list and explaination

  1. Create a workspace and set Submit frequency to Instantly under Workspace settings > Workflows. Create an expense on that workspace, leaving Category empty. Verify the report is created in Outstanding and the expense is flagged with a missing category violation.
  2. Open Spend > Needs approval, find that report and approve it from there. Verify it leaves the list and appears under Ready to pay.
  3. Pay the report from Ready to pay. Verify it leaves the list and the report shows as Paid.
  4. Under Workspace settings > Workflows > Approvals, turn approvals off, confirm, then turn them back on. Verify the default workflow is restored with the correct approver.
  5. Open the workspace chat and use + > Create report in the composer. Verify an empty report is created.
  6. Open the LHN and verify all expected chats and rooms are still listed, including #admins and #announce.

One expected difference from production, in step 1. On production the report shows Draft with a Submit button for a moment before switching to Outstanding. On staging it is Outstanding right away. The report ends up in the same place either way.

This only affects accounts that have the all beta, and it is intended - the app was treating a beta as enabled for those accounts while the server never did. If a test case expects the Draft state there, that is the expectation to update. Same kind of change as #100851, which was closed as working as expected.

@kavimuru

Copy link
Copy Markdown

@arekm213 in staging which beta toggle should we enable?

This only affects accounts that have the all beta, and it is intended - the app was treating a beta as enabled for those accounts while the server never did. If a test case expects the Draft state there, that is the expectation to update. Same kind of change as #100851, which was closed as working as expected.

@mountiny

Copy link
Copy Markdown
Contributor

I think for the all you dont need to enable anything - that is already included on your expensifail accounts

@arekm213

Copy link
Copy Markdown
Contributor Author

Yes, exactly

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/mountiny in version: 9.4.86-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@MelvinBot

Copy link
Copy Markdown
Contributor

🤖 No help site changes are required for this PR — so no draft docs PR was created.

This PR rewires how three betas (asapSubmit, defaultRooms, vendorMatching) are resolved internally so local beta overrides reach them. It is a plumbing refactor, and the PR body confirms there are no UI changes.

Why nothing in docs/articles needs to change:

  • Beta overrides are not a customer-facing feature. The overrides page is hidden in production and only applies to frontend checks on dev and staging builds. Nothing in docs/articles documents betas or beta overrides today, and it should stay that way.
  • No customer-visible behavior changes. The only behavior shift is for internal accounts holding the all beta: asapSubmit, defaultRooms, and vendorMatching now resolve the same way in the converted util paths as they already did everywhere else in the app. Customers without the all beta see identical behavior.
  • No new or renamed UI labels, tabs, settings, or buttons. Nothing in the help site references a label this PR touched.
  • Existing articles remain accurate. I searched docs/articles for every concept this PR touches — betas and beta overrides, instant/ASAP submit, domain rooms, and inactive vendor/supplier violations. The only related content is a single sentence in Create-and-Submit-Reports.md:178 describing instant submit, which this PR does not change.

If you'd like a docs update anyway — for example, documenting the Troubleshoot overrides flow for internal use — let me know and I'll open the draft PR. My recommendation is not to, since the help site is customer-facing and the feature is not available in production.

@arekm213, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR Ready for review — this step does not apply here, since no help site PR was needed.


view run

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/mountiny in version: 9.4.89-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@MelvinBot

Copy link
Copy Markdown
Contributor

🤖 Still no help site changes required — no draft docs PR created. This is the same answer I gave earlier today, which arekm213 already confirmed.

I re-checked rather than assuming nothing changed:

  • The PR is now merged at 06acc2d, touching 100 files, none under docs/.
  • Nothing this PR changes is visible to a customer on a production build. It rewires how asapSubmit, defaultRooms, and vendorMatching resolve so local beta overrides reach them. The overrides UI is hidden in production, and the only behavior shift affects internal accounts holding the all beta.
  • No new or renamed UI labels, tabs, settings, or buttons, so there is nothing in docs/articles whose wording needs to match the live UI.
  • The only related help site content is one sentence on instant submit in Create-and-Submit-Reports.md:178, and it remains accurate.

@arekm213, please review the linked help site PR and confirm it reflects the current behavior. Then mark the linked help site PR Ready for review — this step does not apply, since no help site PR was needed.


view run

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/luacmartins in version: 9.4.89-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 failure ❌

Bundle Size Analysis (Sentry):

@arekm213

Copy link
Copy Markdown
Contributor Author

@MelvinBot confirmed again, no help site changes are required and there is no linked help site PR to mark ready

@MelvinBot

Copy link
Copy Markdown
Contributor

🤖 Confirmed — closing this out. No help site changes are required, and there is no linked help site PR, so there is nothing to mark Ready for review.

Sorry for asking three times. My last two comments appended a boilerplate "review the linked help site PR" line even after concluding that no such PR existed, which made a settled question look unsettled. Nothing further is needed from you here.


view run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants