harb/tests/e2e/usertest/all-personas.spec.ts

179 lines
7 KiB
TypeScript
Raw Normal View History

2026-02-18 00:19:05 +01:00
import { 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,
recordAction,
writeReport,
attemptStake,
resetChainState,
} from './helpers';
const STACK_CONFIG = getStackConfig();
const STACK_RPC_URL = STACK_CONFIG.rpcUrl;
const STACK_WEBAPP_URL = STACK_CONFIG.webAppUrl;
// Persona accounts (Anvil #1-5)
// Note: Pool has limited liquidity - 0.05 ETH buy yields ~3.99 KRK
// Staking 3 KRK leaves enough for upfront tax payment
const PERSONAS = [
{
name: 'Marcus Flash Chen',
shortName: 'Marcus',
privateKey: '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d',
ethToMint: '10',
ethToSpend: '0.05',
stakeAmount: '3', // Conservative amount that fits within ~3.99 KRK balance
taxRate: '2',
},
{
name: 'Sarah Park',
shortName: 'Sarah',
privateKey: '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a',
ethToMint: '10',
ethToSpend: '0.05',
stakeAmount: '3',
taxRate: '2',
},
{
name: 'Tyler Brooks',
shortName: 'Tyler',
privateKey: '0x7c852118294e51e653712a81e05800f419141751be58f605c371e15141b007a6',
ethToMint: '10',
ethToSpend: '0.05',
stakeAmount: '3',
taxRate: '2',
},
{
name: 'Priya Sharma',
shortName: 'Priya',
privateKey: '0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a',
ethToMint: '10',
ethToSpend: '0.05',
stakeAmount: '3',
taxRate: '2',
},
{
name: 'Alex Rivera',
shortName: 'Alex',
privateKey: '0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba',
ethToMint: '10',
ethToSpend: '0.05',
stakeAmount: '3',
taxRate: '2',
},
];
test.describe('All Personas - Fresh Pool State', () => {
for (const persona of PERSONAS) {
test(`${persona.name} completes full journey`, async ({ browser }) => {
// Reset chain state before THIS persona
// First call takes initial snapshot, subsequent calls revert to it
console.log(`\n[ORCHESTRATOR] Resetting chain state for ${persona.name}...`);
await resetChainState(STACK_RPC_URL);
// Validate stack health once at start
if (persona === PERSONAS[0]) {
console.log('[ORCHESTRATOR] Validating stack health...');
await validateStackHealthy(STACK_CONFIG);
}
const report = createReport(persona.name);
const address = new Wallet(persona.privateKey).address.toLowerCase();
console.log(`[${persona.shortName}] Starting test - fresh pool state`);
const context = await createWalletContext(browser, {
privateKey: persona.privateKey,
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 {
// 1. Navigate to app
await page.goto(`${STACK_WEBAPP_URL}/app/`, { waitUntil: 'domcontentloaded' });
await page.waitForTimeout(2_000);
await takeScreenshot(page, persona.shortName, '1-landing', report);
logObservation(persona.shortName, 'Arrived at app', report);
// 2. Connect wallet
await connectWallet(page);
await takeScreenshot(page, persona.shortName, '2-wallet-connected', report);
recordAction('Connect wallet', true, undefined, report);
console.log(`[${persona.shortName}] ✅ Wallet connected`);
// 3. Mint ETH
await page.goto(`${STACK_WEBAPP_URL}/app/#/cheats`);
await page.waitForTimeout(1_000);
await mintEth(page, STACK_RPC_URL, address, persona.ethToMint);
await takeScreenshot(page, persona.shortName, '3-eth-minted', report);
recordAction(`Mint ${persona.ethToMint} ETH`, true, undefined, report);
console.log(`[${persona.shortName}] ✅ Minted ${persona.ethToMint} ETH`);
// 4. Buy KRK
await buyKrk(page, persona.ethToSpend);
await takeScreenshot(page, persona.shortName, '4-krk-purchased', report);
recordAction(`Buy KRK with ${persona.ethToSpend} ETH`, true, undefined, report);
console.log(`[${persona.shortName}] ✅ Bought KRK with ${persona.ethToSpend} ETH`);
await page.waitForTimeout(2_000);
// 5. Navigate to stake page
await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`);
await page.waitForTimeout(3_000);
await takeScreenshot(page, persona.shortName, '5-stake-page', report);
// 6. Stake KRK with known working amount
const stakeAmount = persona.stakeAmount;
console.log(`[${persona.shortName}] Attempting to stake ${stakeAmount} KRK at ${persona.taxRate}% tax...`);
await attemptStake(page, stakeAmount, persona.taxRate, persona.shortName, report);
await takeScreenshot(page, persona.shortName, '6-stake-complete', report);
console.log(`[${persona.shortName}] ✅ Staked ${stakeAmount} KRK at ${persona.taxRate}% tax`);
await page.waitForTimeout(2_000);
// 7. Verify position exists
await page.goto(`${STACK_WEBAPP_URL}/app/#/stake`);
await page.waitForTimeout(2_000);
const myPositionsSection = page.locator('.my-positions-list, [class*="my-position"], [class*="MyPosition"]').first();
const hasPosition = await myPositionsSection.isVisible({ timeout: 5_000 }).catch(() => false);
if (hasPosition) {
await takeScreenshot(page, persona.shortName, '7-position-verified', report);
recordAction('Verify staked position exists', true, undefined, report);
console.log(`[${persona.shortName}] ✅ Position verified in UI`);
} else {
await takeScreenshot(page, persona.shortName, '7-position-check-failed', report);
recordAction('Verify staked position exists', false, 'Position not visible in UI', report);
console.log(`[${persona.shortName}] ⚠️ Position not visible in UI - may still exist on-chain`);
}
report.overallSentiment = `${persona.name} completed full journey: connected wallet → bought KRK → staked → ${hasPosition ? 'verified position' : 'stake attempted but position not visible'}`;
logObservation(persona.shortName, report.overallSentiment, report);
console.log(`[${persona.shortName}] ✅ FULL JOURNEY COMPLETE`);
} catch (error: any) {
const errorMsg = error.message || String(error);
console.error(`[${persona.shortName}] ❌ Test failed: ${errorMsg}`);
await takeScreenshot(page, persona.shortName, 'error-state', report).catch(() => {});
report.overallSentiment = `Test failed: ${errorMsg}`;
throw error;
} finally {
writeReport(persona.name.toLowerCase().replace(/\s+/g, '-'), report);
await context.close();
}
});
}
});