Skip to content

feat(perps): normalize Mobile Perps leverage analytics to previous_leverage - #34813

Merged
abretonc7s merged 1 commit into
mainfrom
TAT-3743-feat-normalize-perps-leverage-analy
Aug 15, 2026
Merged

feat(perps): normalize Mobile Perps leverage analytics to previous_leverage#34813
abretonc7s merged 1 commit into
mainfrom
TAT-3743-feat-normalize-perps-leverage-analy

Conversation

@abretonc7s

@abretonc7s abretonc7s commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Description

Lite and Pro Perps leverage-change events now emit Segment previous_leverage instead of the unknown camelCase previousLeverage. Both forms share PERPS_ANALYTICS_PREVIOUS_LEVERAGE because @metamask/perps-controller does not export that key yet.

Acceptance criteria:

  • AC1 Both Mobile leverage forms emit previous_leverage and no longer emit previousLeverage — PASS
  • AC2 Tests cover the exact analytics property name in Lite and Pro leverage-change flows — PASS
  • AC3 Shared/canonical analytics constant; no camelCase added to the Segment schema — PASS

Changelog

CHANGELOG entry: null

Related issues

Fixes: https://consensyssoftware.atlassian.net/browse/TAT-3743

Manual testing steps

Feature: Perps leverage-change analytics property name

  Scenario: Lite and Pro leverage confirm emit previous_leverage
    Given a funded Perps account on Lite or Pro order form
    When the user confirms a new leverage value
    Then the Perp UI Interaction leverage_changed event includes previous_leverage
    And the payload does not include previousLeverage

Covered by recipe source assertions and unit tests; no visual UI change.

Screenshots/Recordings

Hidden analytics property rename. Proof is recipe state assertions plus unit tests; no user-visible UI change.

Validation Recipe

recipe.json (5 steps — prove previous_leverage on Lite and Pro emitters)
{
  "$schema": "https://farmslot.io/schemas/recipe-v1.schema.json",
  "title": "TAT-3743 normalize Perps leverage analytics to previous_leverage",
  "description": "Prove Lite and Pro leverage-change emitters use the Segment previous_leverage property via the shared mobile constant, and no longer emit camelCase previousLeverage.",
  "workflow": {
    "entry": "assert-constant-file",
    "nodes": {
      "assert-constant-file": {
        "action": "assert_file",
        "path": "app/components/UI/Perps/constants/perpsAnalytics.ts",
        "contains": "previous_leverage",
        "intent": "Confirm the shared analytics constant file declares the Segment previous_leverage key",
        "proves": ["ac3-shared-constant"],
        "next": "scan-emitters"
      },
      "scan-emitters": {
        "action": "command",
        "cmd": "node -e \"var fs=require('fs'); var files=['app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.tsx','app/components/UI/Perps/Views/PerpsProMarketView/components/PerpsProOrderForm/usePerpsProOrderForm.ts']; var errors=[]; files.forEach(function(f){ var s=fs.readFileSync(f,'utf8'); if (s.indexOf('previousLeverage') !== -1) errors.push(f+': still emits previousLeverage'); if (s.indexOf('PERPS_ANALYTICS_PREVIOUS_LEVERAGE') === -1) errors.push(f+': missing PERPS_ANALYTICS_PREVIOUS_LEVERAGE'); }); if (errors.length){ console.error(errors.join('\\n')); process.exit(1);} console.log('lite-and-pro-emitters-use-previous_leverage');\"",
        "intent": "Scan Lite and Pro leverage-change emitters for the shared previous_leverage constant and absence of camelCase",
        "next": "assert-scan-exit"
      },
      "assert-scan-exit": {
        "action": "assert_exit_code",
        "source": "scan-emitters",
        "expected": 0,
        "intent": "Confirm the emitter scan completed successfully",
        "next": "assert-scan-output"
      },
      "assert-scan-output": {
        "action": "assert_output",
        "source": "scan-emitters",
        "stream": "stdout",
        "contains": "lite-and-pro-emitters-use-previous_leverage",
        "intent": "Confirm both leverage forms passed the previous_leverage contract",
        "proves": ["ac1-lite-pro-previous-leverage", "ac3-shared-constant"],
        "next": "done"
      },
      "done": {
        "action": "end",
        "status": "pass"
      }
    }
  },
  "proofTargets": [
    {
      "id": "ac1-lite-pro-previous-leverage",
      "claim": "Lite PerpsOrderView and Pro usePerpsProOrderForm leverage-change payloads emit previous_leverage and do not emit previousLeverage."
    },
    {
      "id": "ac3-shared-constant",
      "claim": "Both emitters use the shared PERPS_ANALYTICS_PREVIOUS_LEVERAGE constant whose value is previous_leverage."
    }
  ]
}

Validation Logs

Command:

MM_HARNESS_BIN=/Users/deeeed/dev/metamask/metamask-harness/bin/mm-harness
unset RECIPE_LIBRARY_PATH
Full output (5/5 passed)
# MetaMask Recipe Run

Status: pass
Duration: 615ms
Nodes: 5/5 passed

## Steps
- PASS assert-constant-file (assert_file, 53ms): path=app/components/UI/Perps/constants/perpsAnalytics.ts
- PASS scan-emitters (command, 120ms): exitCode=0, stdout=lite-and-pro-emitters-use-previous_leverage
- PASS assert-scan-exit (assert_exit_code, 59ms): source=scan-emitters, expected=0, actual=0
- PASS assert-scan-output (assert_output, 58ms): source=scan-emitters, stream=stdout, contains=lite-and-pro-emitters-use-previous_leverage
- PASS done (end, 0ms)

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
    • Ideally on a mid-range device; emulator is acceptable
  • I've tested with a power user scenario
    • Use these power-user SRPs to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production performance metrics

N/A for performance checks: analytics property rename only; no UI or hot-path change. Labels to apply at publish: team-perps, type-feature.

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Low Risk
Analytics payload rename only; no trading, auth, or UI behavior changes.

Overview
Lite and Pro Perps leverage-change PERPS_UI_INTERACTION events now send the Segment field previous_leverage instead of the camelCase previousLeverage.

A shared mobile constant PERPS_ANALYTICS_PREVIOUS_LEVERAGE ('previous_leverage') is used in PerpsOrderView and usePerpsProOrderForm until @metamask/perps-controller exports the key. Unit tests on both flows assert the property name and that previousLeverage is not present.

Reviewed by Cursor Bugbot for commit 2d08480. Bugbot is set up for automated code reviews on this repo. Configure here.

Co-authored-by: Cursor <cursoragent@cursor.com>
@abretonc7s abretonc7s changed the title chore: prepare farmslot publication pkg-675409ea-mssy3mm7 feat(perps): normalize Mobile Perps leverage analytics to previous_leverage Aug 14, 2026
@abretonc7s
abretonc7s marked this pull request as ready for review August 14, 2026 13:48
@abretonc7s
abretonc7s requested a review from a team as a code owner August 14, 2026 13:48
@github-actions

Copy link
Copy Markdown
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@github-actions github-actions Bot added the pr-not-ready-for-e2e Skip E2E and block merging. Remove this label once the PR is ready to run the E2E tests. label Aug 14, 2026
@metamask-ci metamask-ci Bot added the team-perps Perps team label Aug 14, 2026
@github-actions github-actions Bot added size-S risk:low AI analysis: low risk labels Aug 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Flaky unit test detection

Run history flaky detection

View recent run history

Historical failure rate is a hint, not proof — review each suggestion in context. See the flaky-test-detection skill for the full pattern reference and manual audit workflow.

Failures / runs sampled per window:

File 7d 15d 30d
app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.test.tsx 0/128 0/172 0/365
app/components/UI/Perps/Views/PerpsProMarketView/components/PerpsProOrderForm/usePerpsProOrderForm.test.ts 0/128 0/172 0/365

AI-detected flaky patterns

app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.test.tsx

  • J7 — Non-deterministic data: Date.now(), Math.random(), unstubbed network (medium)
    • This test file builds mock market data with hard-coded values inside a stream callback, but the broader file also relies on live stream-style behavior and route-driven rendering. The flaky pattern here is the use of non-deterministic external-style data sources in a test harness without pinning all inputs; if any part of the component under test reads from an unstubbed stream or timing-sensitive subscription, the rendered state can vary between runs. Make the test fully deterministic by stubbing every stream/selector input the component consumes and asserting against fixed, explicit values only.
    • Suggested fix in app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.test.tsx:
      -const mockPrices: Record<string, unknown> = {};
      -        symbols.forEach((symbol: string) => {
      -          mockPrices[symbol] = {
      -            price: '3000',
      -            percentChange24h: '2.5',
      +const mockPrices: Record<string, unknown> = {
      +  ETH: {
      +    price: '3000',
      +    percentChange24h: '2.5',
      +  },
      +};
      +        symbols.forEach((symbol: string) => {
      +          if (!mockPrices[symbol]) {
      +            mockPrices[symbol] = {
      +              price: '3000',
      +              percentChange24h: '2.5',
      +            };
      +          }
      +        });
  • J4 — waitFor without assertion (high)
    • The assertion inside waitFor is using toBeDefined() on a query result rather than a real element-presence assertion. That can let the callback pass before the UI is actually ready, especially when the component updates asynchronously. Use a concrete assertion that fails until the element is truly rendered, such as toBeOnTheScreen() or getBy* inside waitFor.
    • Suggested fix in app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.test.tsx:
      -await waitFor(() => {
      -      expect(screen.getByText('Leverage')).toBeDefined();
      -    });
      +await waitFor(() => {
      +      expect(screen.getByText('Leverage')).toBeOnTheScreen();
      +    });
  • J4 — waitFor without assertion (high)
    • This waitFor block uses toBeDefined() checks instead of asserting actual screen presence, which weakens the synchronization guarantee and can hide races. Replace the presence checks with toBeOnTheScreen() so the wait only resolves once the UI is truly mounted and visible.
    • Suggested fix in app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.test.tsx:
      -await waitFor(() => {
      -      expect(screen.getByTestId('perps-order-header')).toBeDefined();
      -      const assetTitle = screen.getByTestId('perps-order-header-asset-title');
      -      expect(assetTitle).toBeDefined();
      -      expect(assetTitle.props.children).toContain('ETH');
      -    });
      +await waitFor(() => {
      +      expect(screen.getByTestId('perps-order-header')).toBeOnTheScreen();
      +      const assetTitle = screen.getByTestId('perps-order-header-asset-title');
      +      expect(assetTitle).toBeOnTheScreen();
      +      expect(assetTitle.props.children).toContain('ETH');
      +    });
  • J4 — waitFor without assertion (high)
    • The preview shows multiple waitFor blocks that rely on toBeDefined() rather than a real UI assertion. That pattern is flaky because it can resolve before the component has actually finished rendering. Convert these checks to toBeOnTheScreen() or another concrete assertion that reflects the rendered state.
    • Suggested fix in app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.test.tsx:
      -await waitFor(() => {
      -      expect(screen.getByText('Leverage')).toBeDefined();
      -    // ...
      +await waitFor(() => {
      +      expect(screen.getByText('Leverage')).toBeOnTheScreen();
      +    // ...
  • J1 — Missing act() around async state updates (critical)
    • This interaction is wrapped in act(), but the callback shown is incomplete and does not await the async work triggered by the press. If the handler performs state updates after an awaited promise, the test can race and intermittently fail. Ensure the press is awaited inside act so all resulting updates are flushed before assertions.
    • Suggested fix in app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.test.tsx:
      -await act(async () => {
      -      fireEvent.press(placeOrderButton);
      -    // ...
      +await act(async () => {
      +      fireEvent.press(placeOrderButton);
      +    });
  • J3 — Missing jest.clearAllMocks()/resetAllMocks() between tests (high)
    • This file does clear mocks in beforeEach, which is good, but the preview shows many shared jest.fn() mocks and spy-like helpers used across a very large suite. If any test mutates implementations rather than just call counts, clearAllMocks alone will not restore them and later tests can inherit stale behavior. Prefer resetAllMocks when tests override implementations, or pair clearAllMocks with explicit restoration of any per-test mock implementations.
    • Suggested fix in app/components/UI/Perps/Views/PerpsOrderView/PerpsOrderView.test.tsx:
      -beforeEach(() => {
      -    jest.clearAllMocks();
      -    mockPerpsAdvancedChartEnabled = false;
      +beforeEach(() => {
      +    jest.resetAllMocks();
      +    mockPerpsAdvancedChartEnabled = false;

app/components/UI/Perps/Views/PerpsProMarketView/components/PerpsProOrderForm/usePerpsProOrderForm.test.ts

  • J9 — Module-level mutable let bindings not reset in beforeEach (high)
    • This file uses module-level mutable let bindings to steer mocked behavior across tests. Those values are shared by every test in the file, so any mutation that is not reset in beforeEach can leak into later cases and create order-dependent failures. Reset every mutable module-level flag to its default in beforeEach.
    • Suggested fix in app/components/UI/Perps/Views/PerpsProMarketView/components/PerpsProOrderForm/usePerpsProOrderForm.test.ts:
      -let mockIsEligible = true;
      -let mockExecutionOptions: {
      -    // ...
      +let mockIsEligible = true;
      +let mockExecutionOptions: {
      +    // ...
      +
      +beforeEach(() => {
      +  mockIsEligible = true;
      +  mockExecutionOptions = {};
      +});

This check is informational only and does not block merging.

@abretonc7s
abretonc7s enabled auto-merge August 14, 2026 14:42
@abretonc7s abretonc7s removed the pr-not-ready-for-e2e Skip E2E and block merging. Remove this label once the PR is ready to run the E2E tests. label Aug 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokePerps, SmokeWalletPlatform, SmokeConfirmations
  • Selected Performance tags: None (no tests recommended)
  • Risk Level: low
  • AI Confidence: 90%
click to see 🤖 AI reasoning details

E2E Test Selection:
The PR makes a targeted analytics fix in the Perps feature: replacing a hardcoded camelCase property key previousLeverage with a new constant PERPS_ANALYTICS_PREVIOUS_LEVERAGE (value: previous_leverage) in both PerpsOrderView and usePerpsProOrderForm. A new constants file and corresponding unit tests are added. The functional behavior of the Perps order flow is unchanged — only the Segment analytics event property key name is corrected to match the snake_case schema. No shared components, navigation, or core infrastructure is affected. SmokePerps is selected as the primary tag since the changes are in Perps order components. Per the SmokePerps tag description, SmokeWalletPlatform (Trending section) and SmokeConfirmations (Add Funds deposits are on-chain transactions) must also be selected when SmokePerps is selected.

Performance Test Selection:
The changes only affect analytics event property key naming in Perps components. No performance-sensitive code paths (rendering, data fetching, state management) are modified. No performance tests are warranted.

View GitHub Actions results

@sonarqubecloud

Copy link
Copy Markdown

@abretonc7s
abretonc7s added this pull request to the merge queue Aug 15, 2026
Merged via the queue into main with commit 1b4b627 Aug 15, 2026
179 of 180 checks passed
@abretonc7s
abretonc7s deleted the TAT-3743-feat-normalize-perps-leverage-analy branch August 15, 2026 02:28
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 15, 2026
@metamask-ci metamask-ci Bot added the release-8.9.0 Issue or pull request that will be included in release 8.9.0 label Aug 15, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release-8.9.0 Issue or pull request that will be included in release 8.9.0 risk:low AI analysis: low risk size-S team-perps Perps team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants