@litocodes/persona-test 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -88
- package/index.js +43 -12
- package/package.json +1 -1
- package/personas.js +23 -0
- package/reports/REPORT_Agent_Lagos_2026-01-25T22-39-06.md +8 -0
package/README.md
CHANGED
|
@@ -1,139 +1,123 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🌍 Persona: The Lagos Test
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **Your app works on a MacBook Pro on Fiber. It breaks for the other 5 billion people.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
**Persona** checks if a button is *frustrating*.
|
|
5
|
+
Persona is **Chaos Engineering for UX**. It spawns adversarial AI agents with distinct psychological profiles to attack your app under real-world conditions.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
[](https://www.npmjs.com/package/@litocodes/persona-test)
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 🔥 The Lagos Test
|
|
12
|
+
|
|
13
|
+
Silicon Valley builds software for perfect internet. Persona tests for the **real world**:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Test your site as an emerging market user on 3G
|
|
17
|
+
npx @litocodes/persona-test --url="https://your-site.com" --agent="lagos" --network="lagos-3g"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**What happens:**
|
|
21
|
+
- 🌐 400ms latency, 400 Kbps bandwidth (real Lagos conditions)
|
|
22
|
+
- 📱 Simulated $50 Android phone behavior
|
|
23
|
+
- 🖱️ Double-clicks (because first click "didn't work")
|
|
24
|
+
- 🔄 Aggressive refresh when spinners hang
|
|
25
|
+
- 😤 Immediate distrust of popups and data collection
|
|
11
26
|
|
|
12
27
|
---
|
|
13
28
|
|
|
14
|
-
##
|
|
29
|
+
## 🎭 The Agents
|
|
15
30
|
|
|
16
|
-
| Agent | Personality |
|
|
17
|
-
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
31
|
+
| Agent | Personality | The Test |
|
|
32
|
+
|-------|-------------|----------|
|
|
33
|
+
| 🌍 **Agent Lagos** | Emerging market user on $50 phone, 3G | Survives your bloated JS bundle |
|
|
34
|
+
| 👴 **Grandpa Joe** | 70yo, low tech literacy | Finds the phone number |
|
|
35
|
+
| 🏎️ **Zoomer Zoe** | 20yo, rage-clicks, impatient | Signs up in <5 clicks |
|
|
36
|
+
| 🕵️ **Skeptical Sam** | Privacy paranoid, reads fine print | Finds and rejects cookies |
|
|
37
|
+
| 💀 **Hacker Harry** | Security researcher | SQL injection testing |
|
|
38
|
+
| 🧭 **Explorer Emma** | Methodical, maps everything | Full site exploration |
|
|
23
39
|
|
|
24
40
|
---
|
|
25
41
|
|
|
26
|
-
##
|
|
42
|
+
## 🌐 Network Chaos Modes
|
|
27
43
|
|
|
28
44
|
```bash
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
--network="wifi" # No throttling
|
|
46
|
+
--network="4g" # 4 Mbps, 20ms
|
|
47
|
+
--network="3g" # 1.5 Mbps, 100ms
|
|
48
|
+
--network="lagos-3g" # 400 Kbps, 400ms (Nigerian 3G)
|
|
49
|
+
--network="lagos-tunnel" # 200 Kbps, 800ms (tunnel effect)
|
|
50
|
+
--network="chaos" # 100 Kbps, 1200ms (worst case)
|
|
51
|
+
--network="edge" # 50 Kbps, 800ms (2G)
|
|
35
52
|
```
|
|
36
53
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 🚀 Quick Start
|
|
40
57
|
|
|
41
58
|
```bash
|
|
42
|
-
#
|
|
59
|
+
# Run instantly
|
|
60
|
+
npx @litocodes/persona-test --url="https://your-site.com" --agent="lagos" --network="lagos-3g"
|
|
61
|
+
|
|
62
|
+
# Run ALL agents in parallel (Mission Control)
|
|
63
|
+
npx @litocodes/persona-test --url="https://your-site.com" --all
|
|
64
|
+
|
|
65
|
+
# Set your Cerebras API key (free)
|
|
43
66
|
export CEREBRAS_API_KEY=csk-xxxxx
|
|
44
67
|
```
|
|
45
68
|
|
|
46
69
|
---
|
|
47
70
|
|
|
48
|
-
##
|
|
71
|
+
## 📦 Output
|
|
49
72
|
|
|
50
|
-
Every session
|
|
73
|
+
Every session produces:
|
|
74
|
+
|
|
75
|
+
1. **🎥 Video Recording** - Watch the AI break your site
|
|
76
|
+
2. **📝 QA Report** - AI-generated friction analysis
|
|
77
|
+
3. **📋 Action Log** - Step-by-step decision trail
|
|
51
78
|
|
|
52
79
|
```
|
|
53
|
-
videos/
|
|
54
|
-
|
|
55
|
-
├── ZOOMER_2026-01-25T23-15-00_ABANDONED.webm
|
|
56
|
-
└── BOOMER_2026-01-25T23-20-00_MAX_ACTIONS.webm
|
|
80
|
+
videos/Agent_Lagos_lagos-3g_2026-01-25_ABANDONED.webm
|
|
81
|
+
reports/REPORT_Agent_Lagos_2026-01-25.md
|
|
57
82
|
```
|
|
58
83
|
|
|
59
|
-
**Pink highlight boxes** flash around elements before the AI clicks them — making the decision-making process visible.
|
|
60
|
-
|
|
61
84
|
---
|
|
62
85
|
|
|
63
|
-
##
|
|
86
|
+
## 🎯 Why This Exists
|
|
64
87
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
│ Parser │ ──▶ │ Brain │ ──▶ │ Hand │
|
|
68
|
-
│ (Cheerio) │ │ (Cerebras) │ │(Playwright) │
|
|
69
|
-
└─────────────┘ └─────────────┘ └─────────────┘
|
|
70
|
-
│ │ │
|
|
71
|
-
▼ ▼ ▼
|
|
72
|
-
DOM → Text Decides: Executes:
|
|
73
|
-
[1] BUTTON "Click [3]" page.click()
|
|
74
|
-
[2] INPUT "Type XSS" page.fill()
|
|
75
|
-
[3] LINK "Quit"
|
|
76
|
-
```
|
|
88
|
+
> "If you pass the Lagos Test, you're ready for the world."
|
|
89
|
+
> "If you fail, you're just a US-only toy."
|
|
77
90
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
3. **Hand** uses Playwright to execute actions with human-like behavior
|
|
91
|
+
Most testing tools check if buttons **work**.
|
|
92
|
+
Persona checks if buttons are **frustrating**.
|
|
81
93
|
|
|
82
|
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
94
|
+
We test for:
|
|
95
|
+
- **Confusion**, not correctness
|
|
96
|
+
- **Trust**, not just functionality
|
|
97
|
+
- **Global readiness**, not just US/EU
|
|
86
98
|
|
|
87
99
|
---
|
|
88
100
|
|
|
89
|
-
##
|
|
101
|
+
## 🛠️ CLI Options
|
|
90
102
|
|
|
91
103
|
```bash
|
|
92
104
|
persona --url="<URL>" --agent="<AGENT>" [options]
|
|
93
105
|
|
|
94
106
|
Options:
|
|
95
|
-
--url, -u Target
|
|
96
|
-
--agent, -a
|
|
97
|
-
--
|
|
107
|
+
--url, -u Target URL (required)
|
|
108
|
+
--agent, -a lagos, boomer, zoomer, skeptic, hacker, explorer
|
|
109
|
+
--all Run ALL agents in parallel
|
|
110
|
+
--network, -n wifi, 4g, 3g, lagos-3g, lagos-tunnel, chaos, edge
|
|
111
|
+
--record Record video (default: true)
|
|
98
112
|
--headless Run without visible browser
|
|
99
|
-
--test Test API connection
|
|
100
|
-
--help, -h Show help
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## 🛠️ Development
|
|
106
|
-
|
|
107
|
-
```bash
|
|
108
|
-
# Clone
|
|
109
|
-
git clone https://github.com/litocodes/persona-test
|
|
110
|
-
cd persona-test
|
|
111
|
-
|
|
112
|
-
# Install dependencies
|
|
113
|
-
npm install
|
|
114
|
-
|
|
115
|
-
# Set API key
|
|
116
|
-
echo "CEREBRAS_API_KEY=your-key" > .env
|
|
117
|
-
|
|
118
|
-
# Run locally
|
|
119
|
-
node index.js --url="https://example.com" --agent="zoomer"
|
|
120
113
|
```
|
|
121
114
|
|
|
122
115
|
---
|
|
123
116
|
|
|
124
|
-
## 🎯 Use Cases
|
|
125
|
-
|
|
126
|
-
- **QA Testing**: Find UX issues before users do
|
|
127
|
-
- **Security Audits**: Hacker Harry tests for SQL injection, XSS
|
|
128
|
-
- **Accessibility**: See how confused users navigate your site
|
|
129
|
-
- **Conversion Optimization**: Watch where Zoomers rage-quit your signup flow
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
117
|
## 📜 License
|
|
134
118
|
|
|
135
119
|
MIT © 2026
|
|
136
120
|
|
|
137
121
|
---
|
|
138
122
|
|
|
139
|
-
**Built
|
|
123
|
+
**Built for the next billion users.** 🌍
|
package/index.js
CHANGED
|
@@ -19,30 +19,51 @@ const NETWORK_PRESETS = {
|
|
|
19
19
|
'wifi': null, // No throttling
|
|
20
20
|
'4g': {
|
|
21
21
|
offline: false,
|
|
22
|
-
downloadThroughput: 4 * 1024 * 1024 / 8,
|
|
23
|
-
uploadThroughput: 3 * 1024 * 1024 / 8,
|
|
22
|
+
downloadThroughput: 4 * 1024 * 1024 / 8,
|
|
23
|
+
uploadThroughput: 3 * 1024 * 1024 / 8,
|
|
24
24
|
latency: 20
|
|
25
25
|
},
|
|
26
26
|
'3g': {
|
|
27
27
|
offline: false,
|
|
28
|
-
downloadThroughput: 1.5 * 1024 * 1024 / 8,
|
|
29
|
-
uploadThroughput: 750 * 1024 / 8,
|
|
28
|
+
downloadThroughput: 1.5 * 1024 * 1024 / 8,
|
|
29
|
+
uploadThroughput: 750 * 1024 / 8,
|
|
30
30
|
latency: 100
|
|
31
31
|
},
|
|
32
|
-
'lagos-3g': { //
|
|
32
|
+
'lagos-3g': { // 🌍 The Lagos Test - Real emerging market conditions
|
|
33
33
|
offline: false,
|
|
34
|
-
downloadThroughput: 400 * 1024 / 8, // 400 Kbps
|
|
34
|
+
downloadThroughput: 400 * 1024 / 8, // 400 Kbps
|
|
35
35
|
uploadThroughput: 150 * 1024 / 8, // 150 Kbps
|
|
36
|
-
latency: 400 // High latency
|
|
36
|
+
latency: 400 // High latency (the killer)
|
|
37
|
+
},
|
|
38
|
+
'lagos-tunnel': { // 🚇 The Tunnel Effect - Connection drops, reconnects
|
|
39
|
+
offline: false,
|
|
40
|
+
downloadThroughput: 200 * 1024 / 8, // 200 Kbps (barely usable)
|
|
41
|
+
uploadThroughput: 50 * 1024 / 8, // 50 Kbps
|
|
42
|
+
latency: 800 // Extreme latency
|
|
43
|
+
},
|
|
44
|
+
'chaos': { // 💀 Chaos Mode - The worst case scenario
|
|
45
|
+
offline: false,
|
|
46
|
+
downloadThroughput: 100 * 1024 / 8, // 100 Kbps
|
|
47
|
+
uploadThroughput: 30 * 1024 / 8, // 30 Kbps
|
|
48
|
+
latency: 1200 // Pain
|
|
37
49
|
},
|
|
38
50
|
'edge': { // 2G/Edge
|
|
39
51
|
offline: false,
|
|
40
|
-
downloadThroughput: 50 * 1024 / 8,
|
|
41
|
-
uploadThroughput: 20 * 1024 / 8,
|
|
52
|
+
downloadThroughput: 50 * 1024 / 8,
|
|
53
|
+
uploadThroughput: 20 * 1024 / 8,
|
|
42
54
|
latency: 800
|
|
43
55
|
}
|
|
44
56
|
};
|
|
45
57
|
|
|
58
|
+
// Device simulation presets
|
|
59
|
+
const DEVICE_PRESETS = {
|
|
60
|
+
'macbook': { slowMo: 0 },
|
|
61
|
+
'iphone': { slowMo: 50 },
|
|
62
|
+
'android-mid': { slowMo: 100 },
|
|
63
|
+
'android-budget': { slowMo: 200 }, // $50 phone - noticeable input lag
|
|
64
|
+
'android-chaos': { slowMo: 400 } // $30 phone - painful
|
|
65
|
+
};
|
|
66
|
+
|
|
46
67
|
const argv = minimist(process.argv.slice(2), {
|
|
47
68
|
string: ['url', 'agent', 'network'],
|
|
48
69
|
boolean: ['headless', 'test', 'help', 'record', 'all'],
|
|
@@ -123,22 +144,31 @@ async function runAgent(url, persona, options = {}) {
|
|
|
123
144
|
const actionHistory = [];
|
|
124
145
|
const failedIds = [];
|
|
125
146
|
let outcome = 'MAX_ACTIONS';
|
|
147
|
+
let actionCount = 0;
|
|
126
148
|
|
|
127
149
|
for (let step = 1; step <= persona.maxActions; step++) {
|
|
128
150
|
const html = await page.content();
|
|
129
151
|
const { domMap, elements, elementCount } = parsePage(html, failedIds);
|
|
130
152
|
|
|
131
|
-
if (elementCount === 0) {
|
|
153
|
+
if (elementCount === 0) {
|
|
154
|
+
console.log(` ⚠️ [${persona.name}] No elements found, scrolling...`);
|
|
155
|
+
await page.mouse.wheel(0, 300);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
132
158
|
|
|
133
159
|
const decision = await getAgentAction(domMap, persona, actionHistory);
|
|
160
|
+
actionCount++;
|
|
134
161
|
|
|
135
|
-
console.log(` 🤖 [${persona.name}] Step ${
|
|
162
|
+
console.log(` 🤖 [${persona.name}] Step ${actionCount}: ${decision.action} [${decision.elementId}] - ${decision.reason?.substring(0, 40)}`);
|
|
136
163
|
|
|
137
164
|
if (decision.action === 'done') { outcome = 'SUCCESS'; break; }
|
|
138
165
|
if (decision.action === 'quit') { outcome = 'ABANDONED'; break; }
|
|
139
166
|
|
|
140
167
|
const target = decision.elementId ? findElementById(elements, decision.elementId) : null;
|
|
141
|
-
if (!target)
|
|
168
|
+
if (!target) {
|
|
169
|
+
console.log(` ⚠️ [${persona.name}] Invalid element ID, skipping...`);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
142
172
|
|
|
143
173
|
try {
|
|
144
174
|
const locator = page.locator(target.selector).first();
|
|
@@ -155,6 +185,7 @@ async function runAgent(url, persona, options = {}) {
|
|
|
155
185
|
if (actionHistory.length > 3) actionHistory.shift();
|
|
156
186
|
|
|
157
187
|
} catch (e) {
|
|
188
|
+
console.log(` ⚠️ [${persona.name}] Action failed: ${e.message.split('\n')[0].substring(0, 40)}`);
|
|
158
189
|
failedIds.push(decision.elementId);
|
|
159
190
|
}
|
|
160
191
|
|
package/package.json
CHANGED
package/personas.js
CHANGED
|
@@ -83,6 +83,29 @@ Creates a mental map of the site.`,
|
|
|
83
83
|
goal: "Explore the entire site and understand its structure.",
|
|
84
84
|
maxActions: 20,
|
|
85
85
|
frustrationThreshold: 15
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
lagos: {
|
|
89
|
+
name: "Agent Lagos",
|
|
90
|
+
age: 28,
|
|
91
|
+
behavior: `Emerging market user on a $50 Android phone with unstable 3G.
|
|
92
|
+
DOUBLE-CLICKS everything because the first click "didn't work."
|
|
93
|
+
Refreshes aggressively if any spinner shows for >2 seconds.
|
|
94
|
+
Deeply suspicious of sites asking for personal info - "Is this a scam?"
|
|
95
|
+
Expects things to be SLOW but gets frustrated when they freeze completely.
|
|
96
|
+
Closes modals immediately - "I don't trust popups."
|
|
97
|
+
Looks for WhatsApp contact links instead of email forms.
|
|
98
|
+
Types slowly, one finger, makes typos.
|
|
99
|
+
If the page goes white/blank, assumes internet is down and quits.
|
|
100
|
+
Prefers simple text over fancy animations (they don't load).`,
|
|
101
|
+
goal: "Complete a basic task (signup/purchase) despite terrible network and device.",
|
|
102
|
+
maxActions: 12,
|
|
103
|
+
frustrationThreshold: 4,
|
|
104
|
+
testStrings: {
|
|
105
|
+
email: "user1234@gmail.com",
|
|
106
|
+
phone: "+234801234567",
|
|
107
|
+
name: "Chidi Okonkwo"
|
|
108
|
+
}
|
|
86
109
|
}
|
|
87
110
|
};
|
|
88
111
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# QA Report: Agent Lagos
|
|
2
|
+
|
|
3
|
+
### Test Report: Agent Lagos on Stripe.com
|
|
4
|
+
#### **Result**: 🚫 Fail
|
|
5
|
+
#### **Summary**: Agent Lagos attempted to complete a basic task on Stripe.com but encountered significant friction due to the site's behavior on a slow network and low-end device. The agent's interactions were marred by failed clicks, aggressive refreshing, and a general distrust of the site's requests for personal information. Despite persistence, the agent ultimately failed to complete the task.
|
|
6
|
+
#### **Friction Points**: The agent struggled with double-clicking on elements that didn't respond immediately, refreshing the page when spinners appeared for more than 2 seconds, and being suspicious of popups and personal info requests. Specifically, elements 2, 1, 182, and 15 caused issues.
|
|
7
|
+
#### **Recommendations**: To improve the user experience for agents like Lagos, I recommend adding a clear loading state to indicate when the site is processing requests, and fixing aria-labels to improve accessibility and reduce confusion.
|
|
8
|
+
#### **Persona Quote**: "Is this a scam? Why it no work?!"
|