You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
e2e/farm.spec.ts and e2e/mocks/freighter.ts provide Playwright coverage for the connect → deposit → countdown → unlock flow with a single, fixed mocked Freighter account for the duration of each test. No E2E test simulates the mocked Freighter API returning a different address partway through a session (e.g. after connecting, before or during a deposit) — the exact scenario described in the wallet-session-hardening issue elsewhere in this batch. Given that issue proposes changing StellarWalletContext to detect and react to account switches, this E2E coverage is necessary both to specify the desired behavior precisely (what should the UI show? does it force a disconnect? does it refresh positions for the new account automatically?) and to prevent regressions once implemented.
Acceptance Criteria
Extend e2e/mocks/freighter.ts to support changing the mocked active address after the test has already 'connected', simulating a live account switch.
Add a new Playwright spec (or extend e2e/farm.spec.ts) that connects as account A, switches the mock to account B mid-session, and asserts the app either (a) detects the switch and refreshes all account-scoped UI (balance, positions, credits) for account B, or (b) clearly disconnects/prompts reconnection — whichever behavior is decided in the companion wallet-session issue — rather than silently continuing to display account A's stale data.
Add an assertion that no transaction can be signed/submitted using a mismatched cached address after the switch is detected.
Document the expected behavior in this spec's comments so it doubles as executable specification for the fix.
Relevant Files
e2e/farm.spec.ts — existing E2E flow this new scenario should extend or sit alongside
e2e/mocks/freighter.ts — the mock Freighter implementation that needs to support a mid-test address change
src/context/StellarWalletContext.tsx — the implementation whose account-switch behavior this E2E test specifies and protects
Additional Notes
Confirmed: StellarWalletContext never re-checks publicKey after initial connect
src/context/StellarWalletContext.tsx's publicKey state (L61) is only ever set in three places, all inside connect()/initial-mount logic: L112, L139, L143 (setPublicKey(access.address) / setPublicKey(addr.address)). There is no interval, no addEventListener for any Freighter account-change event, and no re-fetch of getAddress() triggered by anything other than the explicit connect() call. The only recurring/reactive re-check in the whole file is refreshNetworkDetails (L65-77), which is wired to visibilitychange (L170-177) and refreshes networkName only — it never touches publicKey. So today, if Freighter's active account changes while the dApp tab stays open and connected, publicKey in React state is permanently stale until the user manually disconnects/reconnects or reloads the page. This matches #67's framing exactly and confirms there is currently zero code path this E2E test could exercise to detect account switches — the E2E spec described in this issue is effectively also the acceptance test for #67's fix, not just documentation of a gap.
Why the existing mock can't express this today
e2e/mocks/freighter.ts's injectFreighterMock(page, pubKey) (L18-92) bakes pubKey into the addInitScript closure at page-load time (L19, passed as an argument to the injected function) and every REQUEST_PUBLIC_KEY/REQUEST_ACCESS response (L45-50) replies with that same captured pubKey for the lifetime of the page. There is no mechanism to mutate the active address after injection — the closure captures pubKey by value once, at page.addInitScript time, and Playwright's addInitScript runs before any page script, so there's no reference to the live value that a later page.evaluate() call could reach into and change (the switch statement's payload construction happens entirely inside the injected function's own scope, L33-71).
Making the address mutable requires exposing it as a real, external, mutable piece of page state — e.g. stash it on window.__freighterMockState = { pubKey } inside the injected script (L19-91) instead of using the closed-over parameter directly, then add an exported helper like switchFreighterMockAccount(page, newPubKey) that does page.evaluate((addr) => { (window as any).__freighterMockState.pubKey = addr; }, newPubKey). The REQUEST_PUBLIC_KEY/REQUEST_ACCESS cases (L45-50) would then read window.__freighterMockState.pubKey at response time rather than closing over the original pubKey parameter, so a switch mid-session is reflected on the next Freighter API call the app happens to make.
The harder question this issue rightly identifies: what triggers the app to notice
Real Freighter (per its public API) does not push account-change events to the page — there is no window.freighter.on('accountChanged', ...) in the current Freighter extension API used here (@stellar/freighter-api, referenced via the FreighterModule type alias at StellarWalletContext.tsx L15). Detecting a switch therefore requires the dApp to poll — e.g. re-call getAddress() on the same cadence/trigger as refreshNetworkDetails already uses (visibilitychange, L170-177), or on a fixed interval similar to useLeaderboard's REFRESH_MS pattern or useSorobanEvents's 5s poll. This is exactly the kind of "what's the actual mechanism" decision #67 needs to make, and it directly determines what "detects the switch" looks like in this E2E test — worth explicitly noting in the spec's comments (per this issue's own AC #4) which polling trigger is assumed, so the test doesn't silently start failing if #67 picks a different mechanism (e.g. only polls on visibilitychange, so a headless E2E test that never blurs/refocuses the tab would need an explicit page.evaluate(() => document.dispatchEvent(new Event('visibilitychange'))) step to simulate that trigger, or the test needs to simulate a real focus loss via a second page.bringToFront() on a second tab).
Concrete places account-scoped UI would need to refresh once a switch is detected
Grep for publicKey consumers gives the surface area this E2E test's assertions should touch: src/app/farm/page.tsx (position list scoped to publicKey, L95/432), PoolDetailClient.tsx (L97, deposit/unlock eligibility), any leaderboard "your rank" computation (useLeaderboard's connectedRank, src/hooks/useLeaderboard.ts L71-75, which takes publicKey as a parameter) — a switch that isn't detected would leave the leaderboard showing account A's rank highlighted/labeled as "you" while actually connected as account B, a subtle but real correctness bug beyond the farm page alone. Worth broadening the new E2E spec's assertions beyond just farm-page balance/position to also check the leaderboard page if it's reachable in the same session, or at minimum flagging it as follow-up scope.
Testing strategy
Extend e2e/mocks/freighter.ts per the sketch above; keep TEST_PUBLIC_KEY/TEST_ADDRESS_DISPLAY (L3-7) as "account A" and add a second constant, e.g. TEST_PUBLIC_KEY_B/TEST_ADDRESS_DISPLAY_B, for the switched-to account, following the existing naming convention.
New spec: either extend e2e/farm.spec.ts (which already has the full RPC-mocking scaffolding — mockSorobanRpc, XDR fixtures at the top of the file, L1-30 — reusable for account B's balance/position responses) or a new e2e/account-switch.spec.ts importing the shared mockSorobanRpc/mock-Freighter helpers to avoid duplicating the RPC mock setup.
Sequence: connect as account A (existing flow), perform some account-scoped action or just observe account A's state rendered, call the new switchFreighterMockAccount(page, TEST_PUBLIC_KEY_B), trigger whatever detection mechanism StellarWalletContext never detects a Freighter account switch while connected, leaving publicKey stale #67 implements (poll interval elapse via page.clock/Playwright's clock API if the app uses real timers, or a simulated visibilitychange), then assert either: (a) account-scoped UI now reflects account B (balance/position refetched — assert via the mocked RPC call count/args, not just DOM text, to prove a real refetch happened rather than a coincidental stale render), or (b) the app disconnects/prompts reconnection — whichever StellarWalletContext never detects a Freighter account switch while connected, leaving publicKey stale #67 decides.
Critical negative assertion (AC Create Dynamic Leaderboard Showing Top Stakers and Credit Earners #3): after the switch, attempt to trigger a sign/submit flow (e.g. click Unlock) and assert the transaction request sent to the mocked RPC uses account B's address, never a cached account A address — this is the assertion most likely to catch a partial fix where UI text updates but an internal ref/closure still holds the old publicKey.
Confirm this spec is wired into the existing playwright script ("playwright": "playwright test --config e2e/playwright.config.ts", package.json) with no separate include list excluding new files — check e2e/playwright.config.ts's testMatch/testDir glob to be sure a new file (if choosing the new-file approach over extending farm.spec.ts) is actually picked up.
Cross-reference
Directly downstream of #67 ("StellarWalletContext never detects a Freighter account switch while connected, leaving publicKey stale") — this E2E issue cannot be meaningfully completed (beyond writing a currently-failing spec) until #67 decides the detection mechanism and implements it; the two should be treated as one deliverable for review purposes, with this issue's spec serving as #67's acceptance test. Also relevant: #70 ("lockAssets/unlockAssets have no guard against the wallet disconnecting between simulate and sign") is the same family of "wallet state can change out from under an in-flight operation" concern, and #93 (network-mismatch defense-in-depth) is the network-axis version of the same underlying problem this issue addresses on the account axis — a shared "verify wallet state immediately before signing" utility (see #93's addendum) would plausibly cover both.
Problem
e2e/farm.spec.tsande2e/mocks/freighter.tsprovide Playwright coverage for the connect → deposit → countdown → unlock flow with a single, fixed mocked Freighter account for the duration of each test. No E2E test simulates the mocked Freighter API returning a different address partway through a session (e.g. after connecting, before or during a deposit) — the exact scenario described in the wallet-session-hardening issue elsewhere in this batch. Given that issue proposes changingStellarWalletContextto detect and react to account switches, this E2E coverage is necessary both to specify the desired behavior precisely (what should the UI show? does it force a disconnect? does it refresh positions for the new account automatically?) and to prevent regressions once implemented.Acceptance Criteria
e2e/mocks/freighter.tsto support changing the mocked active address after the test has already 'connected', simulating a live account switch.e2e/farm.spec.ts) that connects as account A, switches the mock to account B mid-session, and asserts the app either (a) detects the switch and refreshes all account-scoped UI (balance, positions, credits) for account B, or (b) clearly disconnects/prompts reconnection — whichever behavior is decided in the companion wallet-session issue — rather than silently continuing to display account A's stale data.Relevant Files
e2e/farm.spec.ts— existing E2E flow this new scenario should extend or sit alongsidee2e/mocks/freighter.ts— the mock Freighter implementation that needs to support a mid-test address changesrc/context/StellarWalletContext.tsx— the implementation whose account-switch behavior this E2E test specifies and protectsAdditional Notes
Confirmed:
StellarWalletContextnever re-checkspublicKeyafter initial connectsrc/context/StellarWalletContext.tsx'spublicKeystate (L61) is only ever set in three places, all insideconnect()/initial-mount logic: L112, L139, L143 (setPublicKey(access.address)/setPublicKey(addr.address)). There is no interval, noaddEventListenerfor any Freighter account-change event, and no re-fetch ofgetAddress()triggered by anything other than the explicitconnect()call. The only recurring/reactive re-check in the whole file isrefreshNetworkDetails(L65-77), which is wired tovisibilitychange(L170-177) and refreshesnetworkNameonly — it never touchespublicKey. So today, if Freighter's active account changes while the dApp tab stays open and connected,publicKeyin React state is permanently stale until the user manually disconnects/reconnects or reloads the page. This matches #67's framing exactly and confirms there is currently zero code path this E2E test could exercise to detect account switches — the E2E spec described in this issue is effectively also the acceptance test for #67's fix, not just documentation of a gap.Why the existing mock can't express this today
e2e/mocks/freighter.ts'sinjectFreighterMock(page, pubKey)(L18-92) bakespubKeyinto theaddInitScriptclosure at page-load time (L19, passed as an argument to the injected function) and everyREQUEST_PUBLIC_KEY/REQUEST_ACCESSresponse (L45-50) replies with that same capturedpubKeyfor the lifetime of the page. There is no mechanism to mutate the active address after injection — the closure capturespubKeyby value once, atpage.addInitScripttime, and Playwright'saddInitScriptruns before any page script, so there's no reference to the live value that a laterpage.evaluate()call could reach into and change (the switch statement'spayloadconstruction happens entirely inside the injected function's own scope, L33-71).Making the address mutable requires exposing it as a real, external, mutable piece of page state — e.g. stash it on
window.__freighterMockState = { pubKey }inside the injected script (L19-91) instead of using the closed-over parameter directly, then add an exported helper likeswitchFreighterMockAccount(page, newPubKey)that doespage.evaluate((addr) => { (window as any).__freighterMockState.pubKey = addr; }, newPubKey). TheREQUEST_PUBLIC_KEY/REQUEST_ACCESScases (L45-50) would then readwindow.__freighterMockState.pubKeyat response time rather than closing over the originalpubKeyparameter, so a switch mid-session is reflected on the next Freighter API call the app happens to make.The harder question this issue rightly identifies: what triggers the app to notice
Real Freighter (per its public API) does not push account-change events to the page — there is no
window.freighter.on('accountChanged', ...)in the current Freighter extension API used here (@stellar/freighter-api, referenced via theFreighterModuletype alias atStellarWalletContext.tsxL15). Detecting a switch therefore requires the dApp to poll — e.g. re-callgetAddress()on the same cadence/trigger asrefreshNetworkDetailsalready uses (visibilitychange, L170-177), or on a fixed interval similar touseLeaderboard'sREFRESH_MSpattern oruseSorobanEvents's 5s poll. This is exactly the kind of "what's the actual mechanism" decision #67 needs to make, and it directly determines what "detects the switch" looks like in this E2E test — worth explicitly noting in the spec's comments (per this issue's own AC #4) which polling trigger is assumed, so the test doesn't silently start failing if #67 picks a different mechanism (e.g. only polls onvisibilitychange, so a headless E2E test that never blurs/refocuses the tab would need an explicitpage.evaluate(() => document.dispatchEvent(new Event('visibilitychange')))step to simulate that trigger, or the test needs to simulate a real focus loss via a secondpage.bringToFront()on a second tab).Concrete places account-scoped UI would need to refresh once a switch is detected
Grep for
publicKeyconsumers gives the surface area this E2E test's assertions should touch:src/app/farm/page.tsx(position list scoped topublicKey, L95/432),PoolDetailClient.tsx(L97, deposit/unlock eligibility), any leaderboard "your rank" computation (useLeaderboard'sconnectedRank,src/hooks/useLeaderboard.tsL71-75, which takespublicKeyas a parameter) — a switch that isn't detected would leave the leaderboard showing account A's rank highlighted/labeled as "you" while actually connected as account B, a subtle but real correctness bug beyond the farm page alone. Worth broadening the new E2E spec's assertions beyond just farm-page balance/position to also check the leaderboard page if it's reachable in the same session, or at minimum flagging it as follow-up scope.Testing strategy
e2e/mocks/freighter.tsper the sketch above; keepTEST_PUBLIC_KEY/TEST_ADDRESS_DISPLAY(L3-7) as "account A" and add a second constant, e.g.TEST_PUBLIC_KEY_B/TEST_ADDRESS_DISPLAY_B, for the switched-to account, following the existing naming convention.e2e/farm.spec.ts(which already has the full RPC-mocking scaffolding —mockSorobanRpc, XDR fixtures at the top of the file, L1-30 — reusable for account B's balance/position responses) or a newe2e/account-switch.spec.tsimporting the sharedmockSorobanRpc/mock-Freighter helpers to avoid duplicating the RPC mock setup.switchFreighterMockAccount(page, TEST_PUBLIC_KEY_B), trigger whatever detection mechanism StellarWalletContext never detects a Freighter account switch while connected, leaving publicKey stale #67 implements (poll interval elapse viapage.clock/Playwright's clock API if the app uses real timers, or a simulatedvisibilitychange), then assert either: (a) account-scoped UI now reflects account B (balance/position refetched — assert via the mocked RPC call count/args, not just DOM text, to prove a real refetch happened rather than a coincidental stale render), or (b) the app disconnects/prompts reconnection — whichever StellarWalletContext never detects a Freighter account switch while connected, leaving publicKey stale #67 decides.publicKey.playwrightscript ("playwright": "playwright test --config e2e/playwright.config.ts", package.json) with no separate include list excluding new files — checke2e/playwright.config.ts'stestMatch/testDirglob to be sure a new file (if choosing the new-file approach over extendingfarm.spec.ts) is actually picked up.Cross-reference
Directly downstream of #67 ("StellarWalletContext never detects a Freighter account switch while connected, leaving publicKey stale") — this E2E issue cannot be meaningfully completed (beyond writing a currently-failing spec) until #67 decides the detection mechanism and implements it; the two should be treated as one deliverable for review purposes, with this issue's spec serving as #67's acceptance test. Also relevant: #70 ("lockAssets/unlockAssets have no guard against the wallet disconnecting between simulate and sign") is the same family of "wallet state can change out from under an in-flight operation" concern, and #93 (network-mismatch defense-in-depth) is the network-axis version of the same underlying problem this issue addresses on the account axis — a shared "verify wallet state immediately before signing" utility (see #93's addendum) would plausibly cover both.