2026-03-03 20:58:01 +00:00
/* eslint-disable no-restricted-syntax -- waitForTimeout: no event source exists for animation settling, wallet connector UI transitions, and debounced state updates in user-simulation tests. See AGENTS.md #Engineering Principles. */
2026-02-18 00:19:05 +01:00
import { expect , test } from '@playwright/test' ;
import { Wallet } from 'ethers' ;
import { createWalletContext } from '../../setup/wallet-provider' ;
import { getStackConfig , validateStackHealthy } from '../../setup/stack' ;
import {
createReport ,
connectWallet ,
mintEth ,
buyKrk ,
takeScreenshot ,
logObservation ,
logCopyFeedback ,
logTokenomicsQuestion ,
recordPageVisit ,
recordAction ,
writeReport ,
attemptStake ,
resetChainState ,
} from './helpers' ;
// Sarah uses Anvil account #2
const ACCOUNT_PRIVATE_KEY = '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a' ;
const ACCOUNT_ADDRESS = new Wallet ( ACCOUNT_PRIVATE_KEY ) . address . toLowerCase ( ) ;
const STACK_CONFIG = getStackConfig ( ) ;
const STACK_RPC_URL = STACK_CONFIG . rpcUrl ;
const STACK_WEBAPP_URL = STACK_CONFIG . webAppUrl ;
test . describe ( 'Sarah Park - Cautious Yield Farmer' , ( ) = > {
test . beforeAll ( async ( ) = > {
await resetChainState ( STACK_RPC_URL ) ;
await validateStackHealthy ( STACK_CONFIG ) ;
} ) ;
test ( 'Sarah researches thoroughly before committing capital' , async ( { browser } ) = > {
const report = createReport ( 'Sarah Park' ) ;
const personaName = 'Sarah' ;
console . log ( ` [ ${ personaName } ] Starting test - Cautious yield farmer seeking sustainable returns... ` ) ;
const context = await createWalletContext ( browser , {
privateKey : ACCOUNT_PRIVATE_KEY ,
rpcUrl : STACK_RPC_URL ,
} ) ;
const page = await context . newPage ( ) ;
page . on ( 'console' , msg = > console . log ( ` [BROWSER] ${ msg . type ( ) } : ${ msg . text ( ) } ` ) ) ;
page . on ( 'pageerror' , error = > console . log ( ` [BROWSER ERROR] ${ error . message } ` ) ) ;
try {
// --- Landing Page (Reads Everything) ---
let pageStart = Date . now ( ) ;
await page . goto ( ` ${ STACK_WEBAPP_URL } /app/ ` , { waitUntil : 'domcontentloaded' } ) ;
await page . waitForTimeout ( 3 _000 ) ;
await takeScreenshot ( page , personaName , 'landing-page' , report ) ;
logObservation ( personaName , 'Reading landing page carefully before connecting wallet' , report ) ;
logCopyFeedback ( personaName , 'Landing page should explain "What is Harberger tax?" in simple terms' , report ) ;
recordPageVisit ( 'Landing' , page . url ( ) , pageStart , report ) ;
// --- Look for About/Docs FIRST ---
logObservation ( personaName , 'Looking for About, Docs, or Team page before doing anything else...' , report ) ;
const hasAbout = await page . getByText ( /about/i ) . first ( ) . isVisible ( ) . catch ( ( ) = > false ) ;
const hasDocs = await page . getByText ( /docs|documentation/i ) . first ( ) . isVisible ( ) . catch ( ( ) = > false ) ;
const hasTeam = await page . getByText ( /team/i ) . first ( ) . isVisible ( ) . catch ( ( ) = > false ) ;
if ( ! hasAbout && ! hasDocs && ! hasTeam ) {
logCopyFeedback ( personaName , 'MAJOR ISSUE: No About, Docs, or Team link visible. I need background info before trusting this.' , report ) ;
logObservation ( personaName , 'Feeling uncertain - no clear educational resources or team transparency' , report ) ;
}
await takeScreenshot ( page , personaName , 'looking-for-info' , report ) ;
// --- Check for Audit Badge ---
const auditVisible = await page . getByText ( /audit/i ) . isVisible ( ) . catch ( ( ) = > false ) ;
if ( ! auditVisible ) {
logCopyFeedback ( personaName , 'No audit badge visible - this is a dealbreaker for me normally, but will test anyway' , report ) ;
logTokenomicsQuestion ( personaName , 'Has this been audited by Certik, Trail of Bits, or similar?' , report ) ;
}
// --- Connect Wallet (Hesitantly) ---
logObservation ( personaName , 'Deciding to connect wallet after reading available info...' , report ) ;
pageStart = Date . now ( ) ;
await connectWallet ( page ) ;
await takeScreenshot ( page , personaName , 'wallet-connected' , report ) ;
recordAction ( 'Connect wallet' , true , undefined , report ) ;
logObservation ( personaName , 'Wallet connected. Now checking the staking interface details.' , report ) ;
// --- Navigate to Stake Page to Learn ---
pageStart = Date . now ( ) ;
2026-02-20 17:28:59 +01:00
await page . goto ( ` ${ STACK_WEBAPP_URL } /app/stake ` ) ;
2026-02-18 00:19:05 +01:00
await page . waitForTimeout ( 3 _000 ) ;
recordPageVisit ( 'Stake (research)' , page . url ( ) , pageStart , report ) ;
await takeScreenshot ( page , personaName , 'stake-page-reading' , report ) ;
logObservation ( personaName , 'Reading staking dashboard carefully - what are these tax rates about?' , report ) ;
logCopyFeedback ( personaName , 'The info icon next to "Staking Dashboard" helps, but needs more detail on risks' , report ) ;
logTokenomicsQuestion ( personaName , 'If I stake at 10% tax, what\'s my expected APY after taxes?' , report ) ;
logTokenomicsQuestion ( personaName , 'What happens if I get snatched? Do I lose my principal or just my position?' , report ) ;
// --- Check Statistics Section ---
const statsSection = page . locator ( '.statistics-wrapper' ) ;
const statsVisible = await statsSection . isVisible ( ) . catch ( ( ) = > false ) ;
if ( statsVisible ) {
await takeScreenshot ( page , personaName , 'statistics-analysis' , report ) ;
logObservation ( personaName , 'Examining statistics - average tax rate, claimed slots, inflation rate' , report ) ;
logTokenomicsQuestion ( personaName , 'How does the 7-day inflation compare to my expected staking returns?' , report ) ;
} else {
logCopyFeedback ( personaName , 'Would be helpful to see protocol statistics and historical data' , report ) ;
}
// --- Mint ETH ---
pageStart = Date . now ( ) ;
2026-02-20 17:28:59 +01:00
await page . goto ( ` ${ STACK_WEBAPP_URL } /app/cheats ` ) ;
2026-02-18 00:19:05 +01:00
await page . waitForTimeout ( 1 _000 ) ;
recordPageVisit ( 'Cheats' , page . url ( ) , pageStart , report ) ;
logObservation ( personaName , 'Using test environment to simulate before committing real funds' , report ) ;
await mintEth ( page , STACK_RPC_URL , ACCOUNT_ADDRESS , '20' ) ;
recordAction ( 'Mint 20 ETH' , true , undefined , report ) ;
// --- Small Test Purchase ---
logObservation ( personaName , 'Starting with a small test purchase to understand the process' , report ) ;
await buyKrk ( page , '0.05' ) ;
recordAction ( 'Buy KRK with 0.05 ETH (test)' , true , undefined , report ) ;
await takeScreenshot ( page , personaName , 'test-purchase-complete' , report ) ;
logObservation ( personaName , 'Test purchase successful. Now buying more for actual staking.' , report ) ;
await page . waitForTimeout ( 2 _000 ) ;
// --- Buy enough for staking (split to reduce slippage) ---
await buyKrk ( page , '3.0' ) ;
recordAction ( 'Buy KRK with 3.0 ETH total' , true , undefined , report ) ;
logObservation ( personaName , 'Bought more KRK. Now ready to stake.' , report ) ;
await page . waitForTimeout ( 2 _000 ) ;
// --- Navigate Back to Stake ---
pageStart = Date . now ( ) ;
2026-02-20 17:28:59 +01:00
await page . goto ( ` ${ STACK_WEBAPP_URL } /app/stake ` ) ;
2026-02-18 00:19:05 +01:00
await page . waitForTimeout ( 2 _000 ) ;
recordPageVisit ( 'Stake (attempt)' , page . url ( ) , pageStart , report ) ;
await takeScreenshot ( page , personaName , 'stake-form-before-fill' , report ) ;
logObservation ( personaName , 'Examining the stake form - trying to understand tax rate implications' , report ) ;
logCopyFeedback ( personaName , 'Tax rate dropdown needs explanation: "What tax rate should I choose?"' , report ) ;
logCopyFeedback ( personaName , 'Would love a calculator: "Stake X at Y% tax = Z estimated APY"' , report ) ;
// --- Conservative Test Stake (High Tax for Safety) ---
logObservation ( personaName , 'Choosing 15% tax rate to minimize snatch risk - prioritizing safety over yield' , report ) ;
logTokenomicsQuestion ( personaName , 'Is 15% tax high enough to prevent snatching? What\'s the meta?' , report ) ;
try {
await attemptStake ( page , '50' , '15' , personaName , report ) ;
await takeScreenshot ( page , personaName , 'conservative-stake-success' , report ) ;
logObservation ( personaName , 'Stake successful! Now monitoring to see if position stays secure.' , report ) ;
recordAction ( 'Stake 50 KRK at 15% tax (conservative)' , true , undefined , report ) ;
} catch ( error : any ) {
logObservation ( personaName , ` Stake failed: ${ error . message } . This is confusing and frustrating. ` , report ) ;
logCopyFeedback ( personaName , 'Error messages need to be clearer and suggest solutions' , report ) ;
await takeScreenshot ( page , personaName , 'stake-error' , report ) ;
}
await page . waitForTimeout ( 3 _000 ) ;
// --- Check Active Positions ---
2026-02-20 17:28:59 +01:00
await page . goto ( ` ${ STACK_WEBAPP_URL } /app/stake ` ) ;
2026-02-18 00:19:05 +01:00
await page . waitForTimeout ( 2 _000 ) ;
await takeScreenshot ( page , personaName , 'checking-my-position' , report ) ;
const activePositions = page . locator ( '.active-positions-wrapper' ) ;
const myPositionVisible = await activePositions . isVisible ( ) . catch ( ( ) = > false ) ;
if ( myPositionVisible ) {
logObservation ( personaName , 'Can see my active position. Would want notifications when something changes.' , report ) ;
logCopyFeedback ( personaName , 'Need mobile notifications or email alerts for position activity (snatch attempts, tax due)' , report ) ;
} else {
logObservation ( personaName , 'Can\'t see my position clearly - where is it? Confusing UX.' , report ) ;
logCopyFeedback ( personaName , '"My Positions" section should be more prominent' , report ) ;
}
// --- Compare to Mental Model (Aave) ---
logObservation ( personaName , 'Comparing this to Aave in my head - Aave is simpler but boring...' , report ) ;
logTokenomicsQuestion ( personaName , 'Aave gives me 8% on USDC with zero snatch risk. Why should I use this instead?' , report ) ;
logCopyFeedback ( personaName , 'Needs a "Why Kraiken?" section comparing to traditional staking/lending' , report ) ;
// --- Final Thoughts ---
await page . waitForTimeout ( 2 _000 ) ;
await takeScreenshot ( page , personaName , 'final-review' , report ) ;
report . overallSentiment = 'Interested but need more information before committing real funds. The Harberger tax mechanism is intriguing but confusing - I don\'t fully understand how to optimize my tax rate or what happens if I get snatched. UI is clean but lacks educational content for newcomers. Missing: audit badge, return calculator, risk disclosures, comparison to alternatives, mobile notifications. Would need to monitor my test stake for 1-2 weeks before scaling up. Compared to Aave (8% risk-free), this needs to offer 10-15% to justify the complexity and snatch risk. Verdict: Promising but not ready for my main capital yet.' ;
logObservation ( personaName , report . overallSentiment , report ) ;
} finally {
writeReport ( 'sarah-park' , report ) ;
await context . close ( ) ;
}
} ) ;
} ) ;