slapify 0.0.14 → 0.0.17
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 +342 -258
- package/dist/ai/interpreter.d.ts +13 -0
- package/dist/ai/interpreter.js +1 -293
- package/dist/browser/agent.js +1 -485
- package/dist/cli.js +1 -1315
- package/dist/config/loader.js +1 -305
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -260
- package/dist/parser/flow.js +1 -117
- package/dist/perf/audit.d.ts +215 -0
- package/dist/perf/audit.js +1 -0
- package/dist/report/generator.d.ts +1 -0
- package/dist/report/generator.js +1 -549
- package/dist/runner/index.d.ts +14 -1
- package/dist/runner/index.js +1 -584
- package/dist/task/index.d.ts +5 -0
- package/dist/task/index.js +1 -0
- package/dist/task/report.d.ts +9 -0
- package/dist/task/report.js +1 -0
- package/dist/task/runner.d.ts +3 -0
- package/dist/task/runner.js +1 -0
- package/dist/task/session.d.ts +18 -0
- package/dist/task/session.js +1 -0
- package/dist/task/tools.d.ts +253 -0
- package/dist/task/tools.js +1 -0
- package/dist/task/types.d.ts +153 -0
- package/dist/task/types.js +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.js +1 -2
- package/package.json +25 -15
- package/dist/ai/interpreter.d.ts.map +0 -1
- package/dist/ai/interpreter.js.map +0 -1
- package/dist/browser/agent.d.ts.map +0 -1
- package/dist/browser/agent.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/config/loader.d.ts.map +0 -1
- package/dist/config/loader.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/parser/flow.d.ts.map +0 -1
- package/dist/parser/flow.js.map +0 -1
- package/dist/report/generator.d.ts.map +0 -1
- package/dist/report/generator.js.map +0 -1
- package/dist/runner/index.d.ts.map +0 -1
- package/dist/runner/index.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,453 +1,537 @@
|
|
|
1
1
|
# slapify 🖐️
|
|
2
2
|
|
|
3
|
-
**AI-powered
|
|
3
|
+
**AI-powered browser automation that slaps** — run autonomous agents, audit performance, and write E2E tests in plain English.
|
|
4
4
|
|
|
5
5
|
By [slaps.dev](https://slaps.dev)
|
|
6
6
|
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What can it do?
|
|
10
|
+
|
|
11
|
+
| Mode | What it does |
|
|
12
|
+
| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| **`task`** | Autonomous AI agent — give it any goal, it figures out how to achieve it. Browses, logs in, schedules itself, audits performance, remembers state across runs. |
|
|
14
|
+
| **`run`** | Execute `.flow` test files — plain-English E2E tests with screenshots and HTML reports |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
7
18
|
## Installation
|
|
8
19
|
|
|
9
20
|
```bash
|
|
10
|
-
# Install globally
|
|
11
21
|
npm install -g slapify
|
|
12
22
|
|
|
13
|
-
#
|
|
23
|
+
# or run without installing
|
|
14
24
|
npx slapify init
|
|
15
25
|
```
|
|
16
26
|
|
|
27
|
+
---
|
|
28
|
+
|
|
17
29
|
## Quick Start
|
|
18
30
|
|
|
19
31
|
```bash
|
|
20
|
-
#
|
|
32
|
+
# 1. Set up your project (interactive — picks LLM, browser, creates sample files)
|
|
21
33
|
slapify init
|
|
22
34
|
|
|
23
|
-
#
|
|
24
|
-
|
|
35
|
+
# 2. Set your API key
|
|
36
|
+
export ANTHROPIC_API_KEY=your-key
|
|
25
37
|
|
|
26
|
-
#
|
|
27
|
-
slapify
|
|
38
|
+
# 3a. Run an autonomous task
|
|
39
|
+
slapify task "Summarise the top posts on reddit.com/r/programming today" --report
|
|
28
40
|
|
|
29
|
-
#
|
|
30
|
-
|
|
41
|
+
# 3b. Run a test flow
|
|
42
|
+
slapify run tests/example.flow --report
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
31
46
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
47
|
+
## Autonomous Agent (`slapify task`)
|
|
48
|
+
|
|
49
|
+
Give it a goal in plain English. The agent decides what to do, browses pages, handles login, retries on errors, and keeps running until the goal is complete — even if that takes hours or days.
|
|
50
|
+
|
|
51
|
+
### CLI
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# One-off research
|
|
55
|
+
slapify task "What is the current gold price?"
|
|
56
|
+
slapify task "Go to reddit.com/r/programming and summarise the top 5 posts"
|
|
57
|
+
slapify task "Check https://myapp.com and tell me if anything looks broken"
|
|
58
|
+
|
|
59
|
+
# Performance audits
|
|
60
|
+
slapify task "Audit the performance of slaps.dev" --report
|
|
61
|
+
slapify task "Audit the home, pricing, and about pages on vercel.com" --report
|
|
62
|
+
|
|
63
|
+
# Long-running / scheduled
|
|
64
|
+
slapify task "Check my LinkedIn messages every 30 minutes and summarise new ones"
|
|
65
|
+
slapify task "Monitor https://example.com/status every 5 minutes and alert if down"
|
|
66
|
+
slapify task "Check BTC price every hour for 24 hours and give me an end-of-day summary"
|
|
67
|
+
|
|
68
|
+
# Auth-required tasks (agent handles login automatically)
|
|
69
|
+
slapify task "Log into myapp.com and export my account data"
|
|
70
|
+
slapify task "Reply to any unread Slack DMs with a friendly holding message"
|
|
71
|
+
|
|
72
|
+
# Flags
|
|
73
|
+
slapify task "..." --report # generate HTML report on exit
|
|
74
|
+
slapify task "..." --headed # show the browser window
|
|
75
|
+
slapify task "..." --debug # verbose logs
|
|
76
|
+
slapify task "..." --save-flow # save steps as a reusable .flow file
|
|
36
77
|
```
|
|
37
78
|
|
|
38
|
-
|
|
79
|
+
### What the agent can do
|
|
80
|
+
|
|
81
|
+
| Capability | Details |
|
|
82
|
+
| ------------------------- | ------------------------------------------------------------------------------ |
|
|
83
|
+
| **Browse & interact** | Navigate, click, type, scroll, fill forms, handle popups |
|
|
84
|
+
| **Bypass bot protection** | Falls back to HTTP-level requests when the browser is blocked |
|
|
85
|
+
| **Login automatically** | Detects login forms, uses saved credential profiles, asks you to save new ones |
|
|
86
|
+
| **Persistent memory** | Stores key facts between runs (thread URLs, last-seen items, etc.) |
|
|
87
|
+
| **Schedule itself** | Creates its own cron jobs for recurring subtasks |
|
|
88
|
+
| **Ask for input** | Pauses and prompts you when it needs information (e.g. OTP, confirmation) |
|
|
89
|
+
| **Performance audit** | Scores, web vitals, network analysis, framework detection, re-render testing |
|
|
90
|
+
| **HTML report** | Full session report with tool timeline, summaries, and perf data |
|
|
39
91
|
|
|
40
|
-
|
|
41
|
-
- Detect installed browsers on your system
|
|
42
|
-
- Let you choose between system Chrome or downloading Chromium
|
|
43
|
-
- Create a sample test file to get started
|
|
92
|
+
### Programmatic (JS/TS)
|
|
44
93
|
|
|
45
|
-
|
|
94
|
+
```typescript
|
|
95
|
+
import { runTask } from "slapify";
|
|
46
96
|
|
|
47
|
-
|
|
97
|
+
// Simple one-shot task
|
|
98
|
+
const result = await runTask({
|
|
99
|
+
goal: "Go to news.ycombinator.com and return the top 10 headlines",
|
|
100
|
+
});
|
|
48
101
|
|
|
102
|
+
console.log(result.finalSummary);
|
|
49
103
|
```
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { runTask, TaskEvent } from "slapify";
|
|
107
|
+
|
|
108
|
+
// With real-time events
|
|
109
|
+
const result = await runTask({
|
|
110
|
+
goal: "Audit the performance of https://myapp.com/pricing and /about",
|
|
111
|
+
|
|
112
|
+
onEvent: (event: TaskEvent) => {
|
|
113
|
+
if (event.type === "message") console.log("Agent:", event.text);
|
|
114
|
+
if (event.type === "status_update") console.log("→", event.message);
|
|
115
|
+
if (event.type === "tool_start") console.log(` [${event.toolName}]`);
|
|
116
|
+
if (event.type === "done") console.log("✅", event.summary);
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
// Called when the agent needs a human answer (e.g. 2FA code, confirmation)
|
|
120
|
+
onHumanInput: async (question, hint) => {
|
|
121
|
+
return await promptUser(question); // plug in your own UI
|
|
122
|
+
},
|
|
123
|
+
});
|
|
58
124
|
```
|
|
59
125
|
|
|
60
|
-
|
|
126
|
+
```typescript
|
|
127
|
+
import { runTask } from "slapify";
|
|
61
128
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
| Credentials | `Login with admin credentials` |
|
|
69
|
-
| Verification | `Verify "Success" message appears` |
|
|
129
|
+
// Resume a previous session (credentials, memory, and context are preserved)
|
|
130
|
+
const result = await runTask({
|
|
131
|
+
goal: "Continue monitoring LinkedIn messages",
|
|
132
|
+
sessionId: "task-2026-02-19T20-19-44-dtbfu",
|
|
133
|
+
});
|
|
134
|
+
```
|
|
70
135
|
|
|
71
|
-
|
|
136
|
+
```typescript
|
|
137
|
+
import { runTask } from "slapify";
|
|
138
|
+
|
|
139
|
+
// Long-running scheduled task — agent sets its own cron internally
|
|
140
|
+
await runTask({
|
|
141
|
+
goal:
|
|
142
|
+
"Every hour, check the BTC price and store it. " +
|
|
143
|
+
"After 24 hours, summarise the day's movements.",
|
|
144
|
+
maxIterations: 500,
|
|
145
|
+
onEvent: (e) => {
|
|
146
|
+
if (e.type === "scheduled") console.log(`Scheduled: ${e.cron}`);
|
|
147
|
+
if (e.type === "sleeping") console.log(`Sleeping until: ${e.until}`);
|
|
148
|
+
if (e.type === "done") console.log(e.summary);
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
```
|
|
72
152
|
|
|
73
|
-
|
|
153
|
+
---
|
|
74
154
|
|
|
75
|
-
|
|
76
|
-
# LLM Settings (required)
|
|
77
|
-
llm:
|
|
78
|
-
provider: anthropic
|
|
79
|
-
model: claude-sonnet-4-20250514
|
|
80
|
-
api_key: ${ANTHROPIC_API_KEY}
|
|
155
|
+
## Performance Auditing
|
|
81
156
|
|
|
82
|
-
|
|
83
|
-
browser:
|
|
84
|
-
headless: true
|
|
85
|
-
timeout: 30000
|
|
86
|
-
viewport:
|
|
87
|
-
width: 1280
|
|
88
|
-
height: 720
|
|
157
|
+
The agent has a built-in `perf_audit` tool. Just ask it to check performance — it navigates, injects observers, collects everything, and includes it all in the HTML report.
|
|
89
158
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
159
|
+
### What's measured
|
|
160
|
+
|
|
161
|
+
| Category | Metrics |
|
|
162
|
+
| --------------------- | ---------------------------------------------------------------- |
|
|
163
|
+
| **Real-user metrics** | FCP, LCP, CLS, TTFB |
|
|
164
|
+
| **Lab scores** | Performance, Accessibility, SEO, Best Practices (0–100) |
|
|
165
|
+
| **Framework** | React / Next.js detection, re-render issues, interaction tests |
|
|
166
|
+
| **Network** | Total size, JS bundle size, CSS, images |
|
|
167
|
+
| **API calls** | Method, URL, status, duration — slow (>500ms) and failed flagged |
|
|
168
|
+
| **Long tasks** | JavaScript blocking the main thread (>50ms) |
|
|
169
|
+
| **Memory** | JS heap usage |
|
|
170
|
+
|
|
171
|
+
### Multi-page comparison
|
|
172
|
+
|
|
173
|
+
Audit multiple pages in one command. The HTML report shows a **tab bar** — one tab per URL, click to switch. Each tab has the full breakdown for that page.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Three-tab report: /, /pricing, /about
|
|
177
|
+
slapify task "Audit the home, pricing, and about pages on vercel.com" --report
|
|
95
178
|
```
|
|
96
179
|
|
|
97
|
-
###
|
|
180
|
+
### Programmatic
|
|
98
181
|
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
type: login-form
|
|
103
|
-
username: ${TEST_USERNAME}
|
|
104
|
-
password: ${TEST_PASSWORD}
|
|
182
|
+
```typescript
|
|
183
|
+
import { runPerfAudit } from "slapify/perf";
|
|
184
|
+
import { BrowserAgent } from "slapify";
|
|
105
185
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
username: admin@example.com
|
|
109
|
-
password: ${ADMIN_PASSWORD}
|
|
110
|
-
totp_secret: JBSWY3DPEHPK3PXP # For 2FA
|
|
186
|
+
const browser = new BrowserAgent();
|
|
187
|
+
await browser.launch();
|
|
111
188
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
189
|
+
const result = await runPerfAudit("https://myapp.com/pricing", browser, {
|
|
190
|
+
lighthouse: true, // lab scores — Performance/A11y/SEO/Best Practices (default: true)
|
|
191
|
+
reactScan: true, // framework detection + re-render analysis (default: true)
|
|
192
|
+
navigate: true, // navigate to the URL before auditing (default: true)
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
console.log(result.vitals); // { fcp, lcp, cls, ttfb }
|
|
196
|
+
console.log(result.scores); // { performance, accessibility, seo, bestPractices }
|
|
197
|
+
console.log(result.react); // { detected, version, issues, interactionTests }
|
|
198
|
+
console.log(result.network); // { totalRequests, totalBytes, apiCalls, longTasks, ... }
|
|
199
|
+
|
|
200
|
+
await browser.close();
|
|
118
201
|
```
|
|
119
202
|
|
|
120
|
-
|
|
203
|
+
---
|
|
121
204
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
205
|
+
## Test Flows (`slapify run`)
|
|
206
|
+
|
|
207
|
+
Write tests as `.flow` files in plain English. The AI interprets each line and executes browser actions, with screenshots and auto-retry on failure.
|
|
208
|
+
|
|
209
|
+
### Smart by default
|
|
210
|
+
|
|
211
|
+
You don't need to spell out every edge case. The runner handles these automatically on every step:
|
|
125
212
|
|
|
126
|
-
|
|
127
|
-
|
|
213
|
+
| What happens automatically | How |
|
|
214
|
+
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
|
|
215
|
+
| **Cookie banners, popups, chat widgets** | Detected and dismissed before they block the next step |
|
|
216
|
+
| **CAPTCHAs** | Detected by page content signals (reCAPTCHA, hCaptcha, Cloudflare Turnstile) and solved automatically |
|
|
217
|
+
| **Credential lookup** | Steps like `Log in` or `Sign in` (no profile named) auto-pick the best matching profile for the current domain, falling back to `default` |
|
|
218
|
+
| **Auto-retry** | Failed steps are retried once before being marked failed |
|
|
128
219
|
|
|
129
|
-
|
|
130
|
-
slapify run tests/login.flow tests/checkout.flow
|
|
220
|
+
### Writing `.flow` files
|
|
131
221
|
|
|
132
|
-
|
|
222
|
+
```
|
|
223
|
+
# tests/checkout.flow
|
|
224
|
+
|
|
225
|
+
Go to https://myshop.com
|
|
226
|
+
[Optional] Close cookie banner
|
|
227
|
+
Click "Add to Cart" on the first product
|
|
228
|
+
Go to checkout
|
|
229
|
+
Log in # picks the right credential profile automatically
|
|
230
|
+
Fill shipping address
|
|
231
|
+
Click "Place Order"
|
|
232
|
+
Verify "Order confirmed" appears
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Syntax reference:**
|
|
236
|
+
|
|
237
|
+
| Syntax | Example |
|
|
238
|
+
| ---------------- | ------------------------------------------------ |
|
|
239
|
+
| Comment | `# This is a comment` |
|
|
240
|
+
| Required step | `Click the login button` |
|
|
241
|
+
| Optional step | `[Optional] Close popup` |
|
|
242
|
+
| Conditional | `If "Accept cookies" appears, click Accept` |
|
|
243
|
+
| Named credential | `Login with admin credentials` |
|
|
244
|
+
| Auto credential | `Log in` — picks best profile for current domain |
|
|
245
|
+
| Assertion | `Verify "Welcome" message appears` |
|
|
246
|
+
|
|
247
|
+
### CLI
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# Run a single flow
|
|
251
|
+
slapify run tests/login.flow
|
|
252
|
+
|
|
253
|
+
# Run all flows in a directory
|
|
133
254
|
slapify run tests/
|
|
134
255
|
|
|
135
|
-
#
|
|
136
|
-
slapify run --headed
|
|
256
|
+
# With visible browser
|
|
257
|
+
slapify run tests/ --headed
|
|
137
258
|
|
|
138
|
-
#
|
|
139
|
-
slapify run --report
|
|
259
|
+
# Generate HTML report with screenshots
|
|
260
|
+
slapify run tests/ --report
|
|
261
|
+
|
|
262
|
+
# Include performance audit in the report
|
|
263
|
+
slapify run tests/ --report --performance
|
|
140
264
|
|
|
141
265
|
# Run in parallel (4 workers by default)
|
|
142
266
|
slapify run tests/ --parallel
|
|
143
267
|
|
|
144
268
|
# Run in parallel with 8 workers
|
|
145
|
-
slapify run tests/
|
|
269
|
+
slapify run tests/ --parallel --workers 8
|
|
146
270
|
|
|
147
|
-
#
|
|
271
|
+
# Auto-fix a failing flow
|
|
148
272
|
slapify fix tests/broken.flow
|
|
149
273
|
|
|
150
|
-
#
|
|
151
|
-
slapify
|
|
274
|
+
# Other utilities
|
|
275
|
+
slapify list # list all flow files
|
|
276
|
+
slapify validate # validate flow syntax
|
|
277
|
+
slapify create my-test # create a blank flow
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Generating flows
|
|
152
281
|
|
|
153
|
-
|
|
154
|
-
slapify list
|
|
282
|
+
Instead of writing flows by hand, use `generate` to have the agent discover the real path by actually running it in the browser — handles login, captcha, and dynamic content automatically, and only records steps that worked.
|
|
155
283
|
|
|
156
|
-
|
|
157
|
-
|
|
284
|
+
```bash
|
|
285
|
+
# Agent runs the goal, saves verified steps to tests/
|
|
286
|
+
slapify generate "test the login flow on github.com"
|
|
158
287
|
|
|
159
|
-
#
|
|
160
|
-
slapify generate "test
|
|
288
|
+
# Save to a specific directory
|
|
289
|
+
slapify generate "test checkout on myshop.com" --dir tests/checkout
|
|
161
290
|
|
|
162
|
-
#
|
|
163
|
-
slapify
|
|
291
|
+
# With visible browser
|
|
292
|
+
slapify generate "sign up flow on myapp.com" --headed
|
|
164
293
|
```
|
|
165
294
|
|
|
166
|
-
|
|
295
|
+
This is equivalent to `slapify task "..." --save-flow --dir tests/` — the saved `.flow` file is proven to work, not just AI-guessed from a page snapshot.
|
|
167
296
|
|
|
168
|
-
###
|
|
297
|
+
### Programmatic (JS/TS)
|
|
169
298
|
|
|
170
299
|
```typescript
|
|
171
300
|
import { Slapify } from "slapify";
|
|
172
301
|
|
|
173
|
-
// Using config directory
|
|
174
302
|
const slapify = new Slapify({ configDir: ".slapify" });
|
|
175
303
|
|
|
176
|
-
// Or with inline config
|
|
177
|
-
const slapify = new Slapify({
|
|
178
|
-
config: {
|
|
179
|
-
llm: {
|
|
180
|
-
provider: "anthropic",
|
|
181
|
-
model: "claude-sonnet-4-20250514",
|
|
182
|
-
api_key: process.env.ANTHROPIC_API_KEY,
|
|
183
|
-
},
|
|
184
|
-
browser: { headless: true },
|
|
185
|
-
},
|
|
186
|
-
});
|
|
187
|
-
|
|
188
304
|
// Run inline steps
|
|
189
305
|
const result = await slapify.run(
|
|
190
306
|
[
|
|
191
307
|
"Go to https://example.com",
|
|
192
|
-
'Click
|
|
308
|
+
'Click "More information"',
|
|
193
309
|
'Verify URL contains "iana.org"',
|
|
194
310
|
],
|
|
195
311
|
"example-test"
|
|
196
312
|
);
|
|
197
313
|
|
|
198
|
-
console.log(result.status); // 'passed'
|
|
199
|
-
console.log(result.passedSteps); //
|
|
200
|
-
console.log(result.duration); //
|
|
314
|
+
console.log(result.status); // 'passed' | 'failed'
|
|
315
|
+
console.log(result.passedSteps); // number
|
|
316
|
+
console.log(result.duration); // ms
|
|
201
317
|
```
|
|
202
318
|
|
|
203
|
-
### Run from File
|
|
204
|
-
|
|
205
319
|
```typescript
|
|
320
|
+
// Run from file
|
|
206
321
|
const result = await slapify.runFile("./tests/checkout.flow");
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### Run Multiple Tests
|
|
210
322
|
|
|
211
|
-
|
|
212
|
-
// Sequential
|
|
323
|
+
// Run multiple — sequential
|
|
213
324
|
const results = await slapify.runMultiple([
|
|
214
325
|
"tests/login.flow",
|
|
215
326
|
"tests/checkout.flow",
|
|
216
327
|
]);
|
|
217
328
|
|
|
218
|
-
//
|
|
329
|
+
// Run multiple — parallel
|
|
219
330
|
const results = await slapify.runMultiple(["tests/"], {
|
|
220
331
|
parallel: true,
|
|
221
332
|
workers: 4,
|
|
222
333
|
});
|
|
223
|
-
|
|
224
|
-
// Run all tests in a directory
|
|
225
|
-
const results = await slapify.runAll("tests/", { parallel: true });
|
|
226
334
|
```
|
|
227
335
|
|
|
228
|
-
### With Progress Callbacks
|
|
229
|
-
|
|
230
336
|
```typescript
|
|
337
|
+
// Step-level callbacks
|
|
231
338
|
const result = await slapify.run(steps, "my-test", {
|
|
232
|
-
onTestStart: (name, totalSteps) =>
|
|
233
|
-
console.log(`Starting ${name}
|
|
234
|
-
|
|
235
|
-
onStep: (stepResult, testName) => {
|
|
339
|
+
onTestStart: (name, totalSteps) =>
|
|
340
|
+
console.log(`Starting ${name} (${totalSteps} steps)`),
|
|
341
|
+
onStep: (stepResult) => {
|
|
236
342
|
const icon = stepResult.status === "passed" ? "✓" : "✗";
|
|
237
343
|
console.log(` ${icon} ${stepResult.step.text}`);
|
|
238
344
|
},
|
|
239
|
-
onTestComplete: (result) => {
|
|
240
|
-
console.log(`Finished: ${result.status}`);
|
|
241
|
-
},
|
|
345
|
+
onTestComplete: (result) => console.log(`Done: ${result.status}`),
|
|
242
346
|
});
|
|
243
347
|
```
|
|
244
348
|
|
|
245
|
-
### Generate Reports
|
|
246
|
-
|
|
247
349
|
```typescript
|
|
248
|
-
//
|
|
249
|
-
const reportPath = slapify.saveReport(result);
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
const suiteReportPath = slapify.saveSuiteReport(results);
|
|
253
|
-
|
|
254
|
-
// Get report HTML as string
|
|
255
|
-
const html = slapify.generateReport(result);
|
|
350
|
+
// Save reports
|
|
351
|
+
const reportPath = slapify.saveReport(result); // single test
|
|
352
|
+
const suiteReport = slapify.saveSuiteReport(results); // full suite
|
|
353
|
+
const html = slapify.generateReport(result); // HTML as string
|
|
256
354
|
```
|
|
257
355
|
|
|
258
|
-
### Jest
|
|
356
|
+
### Jest / Vitest integration
|
|
259
357
|
|
|
260
358
|
```typescript
|
|
261
359
|
import { Slapify } from "slapify";
|
|
262
360
|
|
|
263
|
-
|
|
264
|
-
const slapify = new Slapify({ configDir: ".slapify" });
|
|
265
|
-
|
|
266
|
-
test("user can login", async () => {
|
|
267
|
-
const result = await slapify.run(
|
|
268
|
-
[
|
|
269
|
-
"Go to https://myapp.com/login",
|
|
270
|
-
"Fill email with test@example.com",
|
|
271
|
-
"Fill password with secret123",
|
|
272
|
-
"Click Login button",
|
|
273
|
-
'Verify "Dashboard" appears',
|
|
274
|
-
],
|
|
275
|
-
"login-test"
|
|
276
|
-
);
|
|
277
|
-
|
|
278
|
-
expect(result.status).toBe("passed");
|
|
279
|
-
}, 60000);
|
|
280
|
-
|
|
281
|
-
test("checkout flow works", async () => {
|
|
282
|
-
const result = await slapify.runFile("tests/checkout.flow");
|
|
283
|
-
expect(result.failedSteps).toBe(0);
|
|
284
|
-
}, 120000);
|
|
285
|
-
});
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
### Vitest Integration
|
|
289
|
-
|
|
290
|
-
```typescript
|
|
291
|
-
import { describe, test, expect } from "vitest";
|
|
292
|
-
import { Slapify } from "slapify";
|
|
361
|
+
const slapify = new Slapify({ configDir: ".slapify" });
|
|
293
362
|
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
"Verify results appear",
|
|
305
|
-
]);
|
|
306
|
-
|
|
307
|
-
expect(result.status).toBe("passed");
|
|
308
|
-
},
|
|
309
|
-
{ timeout: 60000 }
|
|
363
|
+
test("user can log in", async () => {
|
|
364
|
+
const result = await slapify.run(
|
|
365
|
+
[
|
|
366
|
+
"Go to https://myapp.com/login",
|
|
367
|
+
"Fill email with test@example.com",
|
|
368
|
+
"Fill password with secret123",
|
|
369
|
+
"Click Login",
|
|
370
|
+
'Verify "Dashboard" appears',
|
|
371
|
+
],
|
|
372
|
+
"login-test"
|
|
310
373
|
);
|
|
311
|
-
|
|
374
|
+
expect(result.status).toBe("passed");
|
|
375
|
+
}, 60_000);
|
|
376
|
+
|
|
377
|
+
test("checkout flow", async () => {
|
|
378
|
+
const result = await slapify.runFile("tests/checkout.flow");
|
|
379
|
+
expect(result.failedSteps).toBe(0);
|
|
380
|
+
}, 120_000);
|
|
312
381
|
```
|
|
313
382
|
|
|
314
|
-
|
|
383
|
+
---
|
|
315
384
|
|
|
316
|
-
|
|
317
|
-
2. **Interpret**: AI interprets each step and determines browser actions
|
|
318
|
-
3. **Execute**: Browser automation executes the actions
|
|
319
|
-
4. **Report**: Results are collected and formatted
|
|
385
|
+
## Credential Management
|
|
320
386
|
|
|
321
|
-
|
|
387
|
+
Slapify handles credentials for any site — login forms, OAuth, or injecting an existing session.
|
|
322
388
|
|
|
323
|
-
|
|
389
|
+
### `.slapify/credentials.yaml`
|
|
324
390
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
-
|
|
329
|
-
|
|
391
|
+
```yaml
|
|
392
|
+
profiles:
|
|
393
|
+
default:
|
|
394
|
+
type: login-form
|
|
395
|
+
username: ${TEST_USERNAME}
|
|
396
|
+
password: ${TEST_PASSWORD}
|
|
330
397
|
|
|
331
|
-
|
|
398
|
+
admin:
|
|
399
|
+
type: login-form
|
|
400
|
+
username: admin@example.com
|
|
401
|
+
password: ${ADMIN_PASSWORD}
|
|
402
|
+
totp_secret: JBSWY3DPEHPK3PXP # TOTP 2FA
|
|
332
403
|
|
|
333
|
-
|
|
404
|
+
# Skip login entirely — inject an existing session
|
|
405
|
+
my-session:
|
|
406
|
+
type: inject
|
|
407
|
+
cookies:
|
|
408
|
+
- name: auth_token
|
|
409
|
+
value: ${AUTH_TOKEN}
|
|
410
|
+
domain: .example.com
|
|
411
|
+
localStorage:
|
|
412
|
+
user_id: "12345"
|
|
413
|
+
theme: dark
|
|
414
|
+
```
|
|
334
415
|
|
|
335
|
-
|
|
416
|
+
In **task mode**, credentials are fully automatic:
|
|
336
417
|
|
|
337
|
-
-
|
|
338
|
-
-
|
|
339
|
-
-
|
|
418
|
+
- The agent detects login forms and picks the right profile
|
|
419
|
+
- If no profile matches, it asks you to enter credentials and offers to save them
|
|
420
|
+
- Saved sessions are reused on future runs — no re-login needed
|
|
340
421
|
|
|
341
|
-
|
|
422
|
+
---
|
|
342
423
|
|
|
343
|
-
|
|
424
|
+
## Configuration
|
|
344
425
|
|
|
345
|
-
|
|
346
|
-
slapify fix tests/broken.flow
|
|
347
|
-
```
|
|
426
|
+
### `.slapify/config.yaml`
|
|
348
427
|
|
|
349
|
-
|
|
428
|
+
```yaml
|
|
429
|
+
llm:
|
|
430
|
+
provider: anthropic
|
|
431
|
+
model: claude-haiku-4-5-20251001 # fast & cheap — recommended
|
|
432
|
+
api_key: ${ANTHROPIC_API_KEY}
|
|
350
433
|
|
|
351
|
-
|
|
434
|
+
browser:
|
|
435
|
+
headless: true
|
|
436
|
+
timeout: 30000
|
|
437
|
+
viewport:
|
|
438
|
+
width: 1280
|
|
439
|
+
height: 720
|
|
352
440
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
-
|
|
357
|
-
|
|
358
|
-
|
|
441
|
+
report:
|
|
442
|
+
format: html
|
|
443
|
+
screenshots: true
|
|
444
|
+
output_dir: ./test-reports
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
---
|
|
359
448
|
|
|
360
449
|
## LLM Providers
|
|
361
450
|
|
|
362
|
-
Supports 6
|
|
451
|
+
Supports 6 providers via Vercel AI SDK.
|
|
363
452
|
|
|
364
|
-
###
|
|
453
|
+
### Budget-friendly (recommended)
|
|
365
454
|
|
|
366
455
|
```yaml
|
|
367
|
-
# Anthropic
|
|
456
|
+
# Anthropic — Claude Haiku 4.5 (fast, ~$1/5M tokens)
|
|
368
457
|
llm:
|
|
369
458
|
provider: anthropic
|
|
370
459
|
model: claude-haiku-4-5-20251001
|
|
371
460
|
api_key: ${ANTHROPIC_API_KEY}
|
|
372
461
|
|
|
373
|
-
# OpenAI
|
|
462
|
+
# OpenAI — GPT-4o Mini
|
|
374
463
|
llm:
|
|
375
464
|
provider: openai
|
|
376
465
|
model: gpt-4o-mini
|
|
377
466
|
api_key: ${OPENAI_API_KEY}
|
|
378
467
|
|
|
379
|
-
# Google
|
|
468
|
+
# Google — Gemini 2.0 Flash (generous free tier)
|
|
380
469
|
llm:
|
|
381
470
|
provider: google
|
|
382
471
|
model: gemini-2.0-flash
|
|
383
472
|
api_key: ${GOOGLE_API_KEY}
|
|
384
473
|
|
|
385
|
-
# Groq
|
|
474
|
+
# Groq — free tier available
|
|
386
475
|
llm:
|
|
387
476
|
provider: groq
|
|
388
477
|
model: llama-3.3-70b-versatile
|
|
389
478
|
api_key: ${GROQ_API_KEY}
|
|
390
479
|
```
|
|
391
480
|
|
|
392
|
-
### More
|
|
481
|
+
### More capable
|
|
393
482
|
|
|
394
483
|
```yaml
|
|
395
|
-
# Anthropic
|
|
484
|
+
# Anthropic — Claude Sonnet
|
|
396
485
|
llm:
|
|
397
486
|
provider: anthropic
|
|
398
487
|
model: claude-sonnet-4-20250514
|
|
399
488
|
|
|
400
|
-
# OpenAI
|
|
489
|
+
# OpenAI — GPT-4o
|
|
401
490
|
llm:
|
|
402
491
|
provider: openai
|
|
403
492
|
model: gpt-4o
|
|
404
493
|
```
|
|
405
494
|
|
|
406
|
-
### Local (
|
|
495
|
+
### Local (no API key)
|
|
407
496
|
|
|
408
497
|
```yaml
|
|
409
|
-
# Ollama
|
|
498
|
+
# Ollama — runs on your machine, completely free
|
|
410
499
|
llm:
|
|
411
500
|
provider: ollama
|
|
412
501
|
model: llama3 # or mistral, codellama, phi3
|
|
413
502
|
base_url: http://localhost:11434/v1
|
|
414
503
|
```
|
|
415
504
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
```yaml
|
|
419
|
-
# Mistral
|
|
420
|
-
llm:
|
|
421
|
-
provider: mistral
|
|
422
|
-
model: mistral-small-latest # or mistral-large-latest
|
|
423
|
-
api_key: ${MISTRAL_API_KEY}
|
|
424
|
-
```
|
|
505
|
+
---
|
|
425
506
|
|
|
426
507
|
## Project Structure
|
|
427
508
|
|
|
428
509
|
```
|
|
429
510
|
your-project/
|
|
430
511
|
├── .slapify/
|
|
431
|
-
│ ├── config.yaml
|
|
432
|
-
│ ├── credentials.yaml
|
|
433
|
-
│ └──
|
|
512
|
+
│ ├── config.yaml # LLM + browser + report settings
|
|
513
|
+
│ ├── credentials.yaml # Saved login profiles
|
|
514
|
+
│ └── tasks/ # Persisted agent sessions (auto-created)
|
|
515
|
+
│ └── task-2026-....jsonl
|
|
434
516
|
├── tests/
|
|
435
517
|
│ ├── auth/
|
|
436
518
|
│ │ ├── login.flow
|
|
437
519
|
│ │ └── signup.flow
|
|
438
|
-
│
|
|
439
|
-
|
|
440
|
-
│ └── smoke.flow
|
|
441
|
-
└── test-reports/
|
|
520
|
+
│ └── checkout.flow
|
|
521
|
+
└── test-reports/ # HTML reports
|
|
442
522
|
└── login-1234567890/
|
|
443
523
|
├── report.html
|
|
444
|
-
└── screenshots
|
|
524
|
+
└── screenshots/
|
|
445
525
|
```
|
|
446
526
|
|
|
527
|
+
---
|
|
528
|
+
|
|
447
529
|
## Requirements
|
|
448
530
|
|
|
449
531
|
- Node.js 18+
|
|
450
|
-
- `agent-browser` (installed
|
|
532
|
+
- `agent-browser` (installed as a peer dependency)
|
|
533
|
+
|
|
534
|
+
---
|
|
451
535
|
|
|
452
536
|
## License
|
|
453
537
|
|