Skip to content

fix: __mocks__/viem.js is incomplete -- many consumers hit formatUnits is not a function on origin/main #616

Description

@Adiz4415

Bug: __mocks__/viem.js is incomplete — many consumers hit (0 , _viem.formatUnits) is not a function

Observed (verbatim from pnpm test --testPathPattern='ReferralLeaderboard' on origin/main)

FAIL src/components/__tests__/ReferralLeaderboard.test.tsx
  ● ReferralLeaderboard › ...
    TypeError: (0 , _viem.formatUnits) is not a function

    > 154 |   formatUnits(BigInt(userRank.totalRewardsEarned), 18)
         |                ^

The error wording (0 , _viem.formatUnits) is the standard CommonJS interop pattern emitted by babel-jest when an import { formatUnits } from 'viem' resolves to an undefined property on the auto-applied manual mock (__mocks__/viem.js).

Reproduction

git checkout origin/main
pnpm test --testPathPattern='ReferralLeaderboard'

Result: every test that mounts ReferralLeaderboard (or any of the other listed consumers) crashes at the first formatUnits(...) call site. Subsequent downstream assertions (getByText('Alice'), getByText('Your Position'), etc.) all fail because the component never renders.

Root cause

__mocks__/viem.js is auto-applied by jest for every test that imports anything from viem. Its current contents are:

module.exports = {
  recoverMessageAddress: jest.fn(() => Promise.resolve('0x123')),
};

So under test, viem resolves to { recoverMessageAddress: jest.fn(...) }. Every other viem API the production code references is undefined at test time.

Production code is correct: require('viem').formatUnits returns a function in viem ^2.44.4 (verified locally — installed version is 2.48.4, the export lives at node_modules/viem/_esm/utils/unit/formatUnits.js and is reachable from the top-level viem namespace). The mismatch is purely at the test/mock boundary.

Affected callers (every formatUnits consumer on origin/main)

File Line(s) First-seen error
src/components/referral/ReferralLeaderboard.tsx 154, 252 yes (the one in the report)
src/components/referral/ClaimRewardsModal.tsx yes
src/components/referral/ReferralStatsCard.tsx yes
src/components/referral/RewardsDisplay.tsx yes
src/components/referral/QuickStats.tsx yes
src/components/GasEstimator.tsx yes

Any future viem API consumer (e.g., formatEther, parseUnits, parseEther, encodeAbiParameters, keccak256, toHex, etc.) added to a component that's imported from a test will hit the same failure mode until the mock is filled out.

Suggested fix

Extend __mocks__/viem.js to provide deterministic stubs for the APIs actually consumed in the codebase. Minimal, faithful example mirroring viem's real semantics:

// __mocks__/viem.js
// Auto-applied by jest for every test that touches `viem` (see jest.config.cjs).
// Production code is unaffected: this only takes effect in the test runner.

const bigIntToDecimalString = (value, decimals) => {
  const bi = typeof value === 'bigint' ? value : BigInt(value);
  const sign = bi < 0n ? '-' : '';
  const abs = bi < 0n ? -bi : bi;
  const base = 10n ** BigInt(decimals);
  const whole = abs / base;
  const fraction = abs % base;
  if (fraction === 0n) return `${sign}${whole}`;
  const fractionStr = fraction.toString().padStart(decimals, '0').replace(/0+$/, '');
  return `${sign}${whole}.${fractionStr}`;
};

module.exports = {
  // existing
  recoverMessageAddress: jest.fn(() => Promise.resolve('0x123')),

  // unit formatting (consumed by 6 components today)
  formatUnits: jest.fn((value, decimals = 18) =>
    bigIntToDecimalString(value, decimals)
  ),
  formatEther: jest.fn((value) => bigIntToDecimalString(value, 18)),
  parseUnits: jest.fn((value, decimals = 18) => {
    const [whole, fraction = ''] = String(value).split('.');
    const padded = (fraction + '0'.repeat(decimals)).slice(0, decimals);
    return BigInt((whole || '0') + padded);
  }),

  // future-proofing for viem APIs the codebase is likely to adopt
  keccak256: jest.fn(() => '0x' + '0'.repeat(64)),
  toHex: jest.fn((v) => '0x' + Buffer.from(String(v)).toString('hex')),
};

Acceptance criteria

  • pnpm test --testPathPattern='ReferralLeaderboard' passes (no TypeError at line 154 or 252).
  • The other 5 consumer test suites also pass without crashing on formatUnits.
  • Production code is unchanged — node -e "require('viem').formatUnits('12345',3)" still returns the live viem result when the mock is not loaded.
  • CI gate (jest) stays at currently-green status; no introduction of new test flakes.

Why a separate issue (not a PR with PR #612)

PR #612 (MettaChain/PropChain-FrontEnd#612, branch perf/stellar-wave-adiz4415-503-506) is already touching 11 files in the same Stellar Wave perf tranche and is on its way to merge. Folding a fix for an orthogonal test-mock gap into that PR would obscure the perf narrative in review. Filing here so a focused PR can target just __mocks__/viem.js (and any related fixtures) without scope creep.

Suggestion for the assignee

  • Run grep -rn "from 'viem'" src tests to enumerate every viem-named-import consumer. Each API referenced is a candidate stub for __mocks__/viem.js.
  • After extending the mock, run pnpm test (full suite) and confirm no (0 , _viem.X) is not a function regressions on any previously-passing suite.
  • Optionally, add a tiny __mocks__/viem.test.ts that asserts the mock returns deterministic values for representative inputs, so future viem-API additions can't quietly regress again.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions