2026-03-03 20:58:01 +00:00
/* eslint-disable no-restricted-syntax -- waitForTimeout: no event source exists for Vue component animation settling and wagmi wallet connector state transitions. See AGENTS.md #Engineering Principles. */
2026-03-02 05:21:24 +00:00
/ * *
* Shared wallet helpers for holdout scenarios .
*
* Functions here operate in the Playwright Node . js context ( not the browser ) .
* UI interactions use Playwright locators ; on - chain reads use direct RPC calls
* so that tests do not depend on the app ' s wallet state for balance queries .
* /
import type { Page } from '@playwright/test' ;
import { expect } from '@playwright/test' ;
2026-03-02 05:59:21 +00:00
import { rpcCall } from './rpc' ;
2026-03-02 05:21:24 +00:00
// ── Balance readers ──────────────────────────────────────────────────────────
/** Return the native ETH balance (in wei) of `address`. */
export async function getEthBalance ( rpcUrl : string , address : string ) : Promise < bigint > {
const result = ( await rpcCall ( rpcUrl , 'eth_getBalance' , [ address , 'latest' ] ) ) as string ;
return BigInt ( result ) ;
}
/** Return the ERC-20 KRK balance (in wei) of `address` on the given token contract. */
export async function getKrkBalance ( rpcUrl : string , krkAddress : string , address : string ) : Promise < bigint > {
const selector = '0x70a08231' ; // balanceOf(address)
const data = selector + address . slice ( 2 ) . padStart ( 64 , '0' ) ;
const result = ( await rpcCall ( rpcUrl , 'eth_call' , [ { to : krkAddress , data } , 'latest' ] ) ) as string ;
return BigInt ( result ) ;
}
// ── UI helpers ───────────────────────────────────────────────────────────────
/ * *
* Connect the test wallet via the desktop UI flow .
*
* Expects the page to already be on the web app with the navbar visible .
* The injected wallet provider must already be set up by createWalletContext .
* Verifies connection by waiting for the connected button to appear in the navbar .
* /
export async function connectWallet ( page : Page ) : Promise < void > {
// Trigger resize so Vue's useMobile composable re-evaluates with screen.width=1280.
await page . evaluate ( ( ) = > window . dispatchEvent ( new Event ( 'resize' ) ) ) ;
await page . waitForTimeout ( 2 _000 ) ;
2026-03-02 05:59:21 +00:00
const screenWidth = await page . evaluate ( ( ) = > window . screen . width ) ;
console . log ( ` [wallet] screen.width = ${ screenWidth } ` ) ;
2026-03-25 09:29:53 +00:00
// Check if wallet is already connected (e.g. wagmi auto-reconnected from storage)
const connectedButton = page . locator ( '.connect-button--connected' ) . first ( ) ;
if ( await connectedButton . isVisible ( { timeout : 1_000 } ) . catch ( ( ) = > false ) ) {
console . log ( '[wallet] Wallet already connected (auto-reconnect)' ) ;
return ;
}
2026-03-02 05:21:24 +00:00
let panelOpened = false ;
const connectButton = page . locator ( '.connect-button--disconnected' ) . first ( ) ;
2026-03-25 09:29:53 +00:00
if ( await connectButton . isVisible ( { timeout : 10_000 } ) ) {
2026-03-02 05:21:24 +00:00
console . log ( '[wallet] Found desktop Connect button, clicking...' ) ;
await connectButton . click ( ) ;
panelOpened = true ;
} else {
// Fallback: mobile login icon. Dead code when screen.width=1280 but kept for safety.
const mobileLoginIcon = page . locator ( '.navbar-end svg' ) . first ( ) ;
if ( await mobileLoginIcon . isVisible ( { timeout : 2_000 } ) ) {
console . log ( '[wallet] Found mobile login icon, clicking...' ) ;
await mobileLoginIcon . click ( ) ;
panelOpened = true ;
}
}
if ( panelOpened ) {
await page . waitForTimeout ( 1 _000 ) ;
const injectedConnector = page . locator ( '.connectors-element' ) . first ( ) ;
if ( await injectedConnector . isVisible ( { timeout : 5_000 } ) ) {
console . log ( '[wallet] Clicking wallet connector...' ) ;
await injectedConnector . click ( ) ;
await page . waitForTimeout ( 2 _000 ) ;
} else {
console . log ( '[wallet] WARNING: No wallet connector found in panel' ) ;
}
}
// The navbar shows .connect-button--connected once wagmi reports status=connected.
await expect ( page . locator ( '.connect-button--connected' ) . first ( ) ) . toBeVisible ( { timeout : 15_000 } ) ;
console . log ( '[wallet] Wallet connected' ) ;
}
/ * *
* Disconnect the wallet by opening the connected panel and clicking the logout icon .
*
* Verifies disconnection by waiting for the Connect button to reappear .
* /
export async function disconnectWallet ( page : Page ) : Promise < void > {
const connectedButton = page . locator ( '.connect-button--connected' ) . first ( ) ;
await expect ( connectedButton ) . toBeVisible ( { timeout : 5_000 } ) ;
await connectedButton . click ( ) ;
// Panel opens showing .connected-header-logout (img alt="Logout").
const logoutIcon = page . locator ( '.connected-header-logout' ) . first ( ) ;
await expect ( logoutIcon ) . toBeVisible ( { timeout : 5_000 } ) ;
await logoutIcon . click ( ) ;
await expect ( page . locator ( '.connect-button--disconnected' ) . first ( ) ) . toBeVisible ( { timeout : 10_000 } ) ;
console . log ( '[wallet] Wallet disconnected' ) ;
}