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'; // Alex uses Anvil account #0 (same as original test, different persona) const ACCOUNT_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'; 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('Alex Rivera - Crypto-Curious Newcomer', () => { test.beforeAll(async () => { await resetChainState(STACK_RPC_URL); await validateStackHealthy(STACK_CONFIG); }); test('Alex learns about DeFi through Kraiken', async ({ browser }) => { const report = createReport('Alex Rivera'); const personaName = 'Alex'; console.log(`[${personaName}] Starting test - Newcomer trying to understand DeFi...`); 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 Carefully) --- 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, 'This looks professional but I have no idea what I\'m looking at...', report); logCopyFeedback(personaName, 'Landing page should have a "New to DeFi?" section that explains basics', report); logTokenomicsQuestion(personaName, 'What is staking? How do I make money from this?', report); recordPageVisit('Landing', page.url(), pageStart, report); // --- Look for Help/Tutorial --- logObservation(personaName, 'Looking for a "How it Works" or tutorial before I do anything...', report); const tutorialVisible = await page.getByText(/how it works|tutorial|getting started|learn/i).isVisible().catch(() => false); if (!tutorialVisible) { logCopyFeedback(personaName, 'CRITICAL: No "Getting Started" guide visible. I\'m intimidated and don\'t know where to begin.', report); logObservation(personaName, 'Feeling overwhelmed - too much jargon without explanation', report); } await takeScreenshot(page, personaName, 'looking-for-help', report); // --- Nervous About Connecting Wallet --- logObservation(personaName, 'I\'ve heard about wallet scams... is this safe to connect?', report); logCopyFeedback(personaName, 'Need trust signals: "Audited", "Secure", "Non-custodial" badges to reassure newcomers', report); const securityInfo = await page.getByText(/secure|safe|audited|trusted/i).isVisible().catch(() => false); if (!securityInfo) { logObservation(personaName, 'No security information visible - makes me nervous to connect wallet', report); } await page.waitForTimeout(2_000); // --- Decides to Connect Wallet (Cautiously) --- logObservation(personaName, 'Okay, deep breath... connecting wallet for the first time on this app', report); try { await connectWallet(page); await takeScreenshot(page, personaName, 'wallet-connected', report); recordAction('Connect wallet (first time)', true, undefined, report); logObservation(personaName, 'Wallet connected! That was easier than I thought. Now what?', report); } catch (error: any) { logObservation(personaName, `Wallet connection failed: ${error.message}. This is too complicated, giving up.`, report); logCopyFeedback(personaName, 'Wallet connection errors need beginner-friendly explanations', report); recordAction('Connect wallet', false, error.message, report); throw error; } // --- Navigate to Stake Page to Learn --- pageStart = Date.now(); await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`); await page.waitForTimeout(3_000); recordPageVisit('Stake (learning)', page.url(), pageStart, report); await takeScreenshot(page, personaName, 'stake-page-first-look', report); logObservation(personaName, 'Lots of numbers and charts... what does it all mean?', report); logTokenomicsQuestion(personaName, 'What is a "Harberger Tax"? Never heard of this before.', report); logTokenomicsQuestion(personaName, 'What are "owner slots"? Is that like shares?', report); // --- Reads Info Icon --- const infoIcon = page.locator('svg').filter({ hasText: /info/i }).first(); const infoVisible = await infoIcon.isVisible().catch(() => false); if (infoVisible) { logObservation(personaName, 'Found info icon - let me read this...', report); // Try to hover to see tooltip await infoIcon.hover().catch(() => {}); await page.waitForTimeout(1_000); await takeScreenshot(page, personaName, 'reading-info-tooltip', report); logCopyFeedback(personaName, 'Info tooltips help, but still too technical for total beginners', report); } else { logCopyFeedback(personaName, 'Need more info icons and tooltips to explain every element', report); } // --- Confused by Terminology --- logObservation(personaName, 'Words I don\'t understand: VWAP, tax rate, snatching, claimed slots...', report); logCopyFeedback(personaName, 'ESSENTIAL: Need a glossary or hover definitions for all DeFi terms', report); // --- Tries to Find FAQ --- logObservation(personaName, 'Looking for FAQ or help section...', report); const faqVisible = await page.getByText(/faq|frequently asked|help/i).isVisible().catch(() => false); if (!faqVisible) { logCopyFeedback(personaName, 'No FAQ visible! Common questions like "Can I lose money?" need answers up front.', report); logTokenomicsQuestion(personaName, 'Can I lose my money if I stake? What are the risks?', report); } // --- Mint ETH (Following Instructions) --- logObservation(personaName, 'I need to get some tokens first... let me figure out how', report); pageStart = Date.now(); await page.goto(`${STACK_WEBAPP_URL}/app/#/cheats`); await page.waitForTimeout(2_000); recordPageVisit('Cheats (confused)', page.url(), pageStart, report); await takeScreenshot(page, personaName, 'cheats-page', report); logObservation(personaName, '"Cheat Console"? Is this for testing? I\'m confused but will try it...', report); try { await mintEth(page, STACK_RPC_URL, ACCOUNT_ADDRESS, '5'); recordAction('Mint 5 ETH (following guide)', true, undefined, report); logObservation(personaName, 'Got some ETH! Still not sure what I\'m doing though...', report); } catch (error: any) { logObservation(personaName, `Mint failed: ${error.message}. These errors are scary!`, report); logCopyFeedback(personaName, 'Error messages should be encouraging, not scary. Add "Need help?" links.', report); recordAction('Mint ETH', false, error.message, report); } // --- Buy Small Amount (Cautiously) --- logObservation(personaName, 'Buying the smallest amount possible to test - don\'t want to lose much if this is a scam', report); try { await buyKrk(page, '0.8'); recordAction('Buy KRK with 0.05 ETH (minimal test)', true, undefined, report); await takeScreenshot(page, personaName, 'small-purchase', report); logObservation(personaName, 'Purchase went through! That was actually pretty smooth.', report); logCopyFeedback(personaName, 'Good: Transaction was straightforward. Bad: No confirmation message explaining what happened.', report); } catch (error: any) { logObservation(personaName, `Buy failed: ${error.message}. Maybe I should just stick to Coinbase...`, report); recordAction('Buy KRK', false, error.message, report); } await page.waitForTimeout(2_000); // --- Navigate to Stake (Intimidated) --- pageStart = Date.now(); await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`); await page.waitForTimeout(2_000); recordPageVisit('Stake (attempting)', page.url(), pageStart, report); await takeScreenshot(page, personaName, 'stake-form-confused', report); logObservation(personaName, 'Staring at the stake form... what tax rate should I pick???', report); logTokenomicsQuestion(personaName, 'Higher tax = more money or less money? This is backwards from normal taxes!', report); logCopyFeedback(personaName, 'CRITICAL: Tax rate needs "Recommended for beginners: 10-15%" guidance', report); // --- Looks for Recommendation --- const recommendationVisible = await page.getByText(/recommended|suggested|beginner/i).isVisible().catch(() => false); if (!recommendationVisible) { logCopyFeedback(personaName, 'Please add a "What should I choose?" helper or wizard mode for newcomers!', report); } // --- Attempt Conservative Stake --- logObservation(personaName, 'Going with 15% because it sounds safe... I think? Really not sure about this.', report); try { await attemptStake(page, '25', '15', personaName, report); await takeScreenshot(page, personaName, 'stake-success', report); logObservation(personaName, 'IT WORKED! I just staked my first crypto! But... what happens now?', report); recordAction('Stake 25 KRK at 15% tax (nervous)', true, undefined, report); logTokenomicsQuestion(personaName, 'When do I get paid? How much will I earn? Where do I see my rewards?', report); } catch (error: any) { logObservation(personaName, `Stake failed: ${error.message}. I give up, this is too hard.`, report); logCopyFeedback(personaName, 'Failed stakes need recovery guidance: "Here\'s what to try next..."', report); await takeScreenshot(page, personaName, 'stake-failed', report); } await page.waitForTimeout(3_000); // --- Check for Progress Indicators --- await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`); await page.waitForTimeout(2_000); await takeScreenshot(page, personaName, 'looking-for-my-position', report); logObservation(personaName, 'Where is my position? How do I see what I earned?', report); logCopyFeedback(personaName, 'Need a big "Your Position" dashboard showing: amount staked, daily earnings, time held', report); // --- Worried About Snatching --- logObservation(personaName, 'I see something about "snatching"... can someone steal my stake???', report); logTokenomicsQuestion(personaName, 'What does snatching mean? Will I lose my money? This is scary!', report); logCopyFeedback(personaName, 'Snatching concept is TERRIFYING for newcomers. Need clear "You don\'t lose principal" message.', report); await takeScreenshot(page, personaName, 'worried-about-snatching', report); // --- Compare to Coinbase --- logObservation(personaName, 'On Coinbase I just click "Stake ETH" and get 4% APY. This is way more complicated...', report); logTokenomicsQuestion(personaName, 'Why should I use this instead of just staking ETH on Coinbase?', report); logCopyFeedback(personaName, 'Need comparison: "Coinbase: 4% simple. Kraiken: 8-15% but you choose your own risk level"', report); // --- Final Feelings --- await page.waitForTimeout(2_000); await takeScreenshot(page, personaName, 'final-state', report); report.overallSentiment = 'Mixed feelings - excited that I did my first DeFi stake, but confused and nervous about many things. GOOD: The actual transaction process was smooth once I figured it out. UI looks professional and trustworthy. CONFUSING: Harberger tax concept is completely foreign to me. Don\'t understand how tax rates affect my earnings. Scared about "snatching" - sounds like I could lose money. No clear guidance on what to do next or how to track earnings. NEEDED: (1) "Getting Started" tutorial with video walkthrough, (2) Glossary of terms in plain English, (3) Tax rate wizard that asks questions and recommends a rate, (4) Big clear "Your Daily Earnings: $X" display, (5) FAQ addressing "Can I lose money?" and "What is snatching?", (6) Comparison to Coinbase/simple staking to show why this is better. VERDICT: Would monitor my tiny stake for a week to see what happens. If I actually earn money and nothing bad happens, I might add more. But if I get "snatched" without understanding why, I\'m selling everything and never coming back. This needs to be MUCH more beginner-friendly to compete with centralized platforms.'; logObservation(personaName, report.overallSentiment, report); } finally { writeReport('alex-rivera', report); await context.close(); } }); });