@phuetz/code-buddy 0.1.0 → 0.1.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/.codebuddy/skills/bundled/brave-search/SKILL.md +490 -0
- package/.codebuddy/skills/bundled/exa-search/SKILL.md +1122 -0
- package/.codebuddy/skills/bundled/perplexity/SKILL.md +748 -0
- package/.codebuddy/skills/bundled/playwright/SKILL.md +520 -0
- package/.codebuddy/skills/bundled/puppeteer/SKILL.md +708 -0
- package/.codebuddy/skills/bundled/web-fetch/SKILL.md +1003 -0
- package/README.md +56 -0
- package/dist/agent/agent-state.d.ts +3 -3
- package/dist/agent/agent-state.js +6 -6
- package/dist/agent/agent-state.js.map +1 -1
- package/dist/agent/base-agent.d.ts +4 -4
- package/dist/agent/base-agent.js +22 -9
- package/dist/agent/base-agent.js.map +1 -1
- package/dist/agent/cache-trace.d.ts +56 -0
- package/dist/agent/cache-trace.js +98 -0
- package/dist/agent/cache-trace.js.map +1 -0
- package/dist/agent/codebuddy-agent.js +3 -2
- package/dist/agent/codebuddy-agent.js.map +1 -1
- package/dist/agent/execution/agent-executor.d.ts +4 -4
- package/dist/agent/execution/agent-executor.js +41 -7
- package/dist/agent/execution/agent-executor.js.map +1 -1
- package/dist/agent/facades/agent-context-facade.js +1 -3
- package/dist/agent/facades/agent-context-facade.js.map +1 -1
- package/dist/agent/facades/message-history-manager.js +14 -12
- package/dist/agent/facades/message-history-manager.js.map +1 -1
- package/dist/agent/facades/session-facade.d.ts +3 -3
- package/dist/agent/facades/session-facade.js +6 -6
- package/dist/agent/facades/session-facade.js.map +1 -1
- package/dist/agent/history-repair.d.ts +37 -0
- package/dist/agent/history-repair.js +124 -0
- package/dist/agent/history-repair.js.map +1 -0
- package/dist/agent/specialized/archive-agent.d.ts +3 -0
- package/dist/agent/specialized/archive-agent.js +71 -31
- package/dist/agent/specialized/archive-agent.js.map +1 -1
- package/dist/agent/specialized/security-review/agent.js +19 -8
- package/dist/agent/specialized/security-review/agent.js.map +1 -1
- package/dist/agent/tool-executor.js +5 -0
- package/dist/agent/tool-executor.js.map +1 -1
- package/dist/agent/turn-diff-tracker.d.ts +79 -0
- package/dist/agent/turn-diff-tracker.js +195 -0
- package/dist/agent/turn-diff-tracker.js.map +1 -0
- package/dist/checkpoints/checkpoint-versioning.js +78 -20
- package/dist/checkpoints/checkpoint-versioning.js.map +1 -1
- package/dist/cli/config-loader.js +2 -4
- package/dist/cli/config-loader.js.map +1 -1
- package/dist/commands/handlers/fcs-handlers.js +1 -1
- package/dist/commands/handlers/fcs-handlers.js.map +1 -1
- package/dist/commands/handlers/memory-handlers.js +2 -1
- package/dist/commands/handlers/memory-handlers.js.map +1 -1
- package/dist/commands/handlers/worktree-handlers.js +11 -0
- package/dist/commands/handlers/worktree-handlers.js.map +1 -1
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/mcp.js +66 -7
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/pipeline.js +25 -13
- package/dist/commands/pipeline.js.map +1 -1
- package/dist/config/model-tools.d.ts +41 -0
- package/dist/config/model-tools.js +194 -0
- package/dist/config/model-tools.js.map +1 -0
- package/dist/context/context-manager-v2.d.ts +2 -1
- package/dist/context/context-manager-v2.js +34 -5
- package/dist/context/context-manager-v2.js.map +1 -1
- package/dist/daemon/daemon-manager.js +23 -19
- package/dist/daemon/daemon-manager.js.map +1 -1
- package/dist/database/database-manager.d.ts +4 -0
- package/dist/database/database-manager.js +16 -7
- package/dist/database/database-manager.js.map +1 -1
- package/dist/desktop-automation/nutjs-provider.js +89 -0
- package/dist/desktop-automation/nutjs-provider.js.map +1 -1
- package/dist/fcs/builtins.d.ts +2 -6
- package/dist/fcs/builtins.js +2 -568
- package/dist/fcs/builtins.js.map +1 -1
- package/dist/fcs/codebuddy-bindings.d.ts +3 -43
- package/dist/fcs/codebuddy-bindings.js +2 -606
- package/dist/fcs/codebuddy-bindings.js.map +1 -1
- package/dist/fcs/index.d.ts +2 -27
- package/dist/fcs/index.js +2 -53
- package/dist/fcs/index.js.map +1 -1
- package/dist/fcs/lexer.d.ts +2 -37
- package/dist/fcs/lexer.js +2 -459
- package/dist/fcs/lexer.js.map +1 -1
- package/dist/fcs/parser.d.ts +2 -68
- package/dist/fcs/parser.js +2 -893
- package/dist/fcs/parser.js.map +1 -1
- package/dist/fcs/runtime.d.ts +2 -59
- package/dist/fcs/runtime.js +2 -623
- package/dist/fcs/runtime.js.map +1 -1
- package/dist/fcs/script-registry.d.ts +3 -69
- package/dist/fcs/script-registry.js +2 -219
- package/dist/fcs/script-registry.js.map +1 -1
- package/dist/fcs/sync-bindings.d.ts +3 -101
- package/dist/fcs/sync-bindings.js +2 -410
- package/dist/fcs/sync-bindings.js.map +1 -1
- package/dist/fcs/types.d.ts +2 -285
- package/dist/fcs/types.js +2 -103
- package/dist/fcs/types.js.map +1 -1
- package/dist/hooks/use-input-handler.d.ts +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/input/voice-control.js +11 -5
- package/dist/input/voice-control.js.map +1 -1
- package/dist/integrations/json-rpc/server.js +5 -5
- package/dist/integrations/json-rpc/server.js.map +1 -1
- package/dist/integrations/mcp/mcp-server.js +1 -1
- package/dist/integrations/mcp/mcp-server.js.map +1 -1
- package/dist/mcp/client.js +2 -1
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/config.js +89 -5
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/mcp-client.js +65 -14
- package/dist/mcp/mcp-client.js.map +1 -1
- package/dist/mcp/transports.d.ts +0 -1
- package/dist/mcp/transports.js +1 -5
- package/dist/mcp/transports.js.map +1 -1
- package/dist/mcp/types.d.ts +2 -0
- package/dist/persistence/session-lock.d.ts +42 -0
- package/dist/persistence/session-lock.js +165 -0
- package/dist/persistence/session-lock.js.map +1 -0
- package/dist/persistence/session-store.d.ts +18 -3
- package/dist/persistence/session-store.js +90 -21
- package/dist/persistence/session-store.js.map +1 -1
- package/dist/plugins/isolated-plugin-runner.d.ts +6 -0
- package/dist/plugins/isolated-plugin-runner.js +19 -1
- package/dist/plugins/isolated-plugin-runner.js.map +1 -1
- package/dist/providers/local-llm-provider.js +21 -4
- package/dist/providers/local-llm-provider.js.map +1 -1
- package/dist/sandbox/docker-sandbox.js +7 -4
- package/dist/sandbox/docker-sandbox.js.map +1 -1
- package/dist/scripting/builtins.d.ts +8 -3
- package/dist/scripting/builtins.js +506 -355
- package/dist/scripting/builtins.js.map +1 -1
- package/dist/scripting/codebuddy-bindings.d.ts +47 -0
- package/dist/scripting/codebuddy-bindings.js +487 -0
- package/dist/scripting/codebuddy-bindings.js.map +1 -0
- package/dist/scripting/index.d.ts +33 -30
- package/dist/scripting/index.js +41 -36
- package/dist/scripting/index.js.map +1 -1
- package/dist/scripting/lexer.d.ts +31 -13
- package/dist/scripting/lexer.js +379 -292
- package/dist/scripting/lexer.js.map +1 -1
- package/dist/scripting/parser.d.ts +63 -44
- package/dist/scripting/parser.js +700 -473
- package/dist/scripting/parser.js.map +1 -1
- package/dist/scripting/runtime.d.ts +55 -24
- package/dist/scripting/runtime.js +600 -288
- package/dist/scripting/runtime.js.map +1 -1
- package/dist/scripting/script-registry.d.ts +54 -0
- package/dist/scripting/script-registry.js +202 -0
- package/dist/scripting/script-registry.js.map +1 -0
- package/dist/scripting/sync-bindings.d.ts +105 -0
- package/dist/scripting/sync-bindings.js +353 -0
- package/dist/scripting/sync-bindings.js.map +1 -0
- package/dist/scripting/types.d.ts +297 -199
- package/dist/scripting/types.js +86 -60
- package/dist/scripting/types.js.map +1 -1
- package/dist/search/usearch-index.js +42 -7
- package/dist/search/usearch-index.js.map +1 -1
- package/dist/security/bash-parser.d.ts +51 -0
- package/dist/security/bash-parser.js +327 -0
- package/dist/security/bash-parser.js.map +1 -0
- package/dist/security/skill-scanner.d.ts +36 -0
- package/dist/security/skill-scanner.js +149 -0
- package/dist/security/skill-scanner.js.map +1 -0
- package/dist/security/trust-folders.d.ts +1 -0
- package/dist/security/trust-folders.js +19 -1
- package/dist/security/trust-folders.js.map +1 -1
- package/dist/server/websocket/handler.js +15 -5
- package/dist/server/websocket/handler.js.map +1 -1
- package/dist/skills/eligibility.js +26 -4
- package/dist/skills/eligibility.js.map +1 -1
- package/dist/tasks/background-tasks.js +5 -1
- package/dist/tasks/background-tasks.js.map +1 -1
- package/dist/tools/apply-patch.d.ts +55 -0
- package/dist/tools/apply-patch.js +273 -0
- package/dist/tools/apply-patch.js.map +1 -0
- package/dist/tools/registry/bash-tools.js +6 -3
- package/dist/tools/registry/bash-tools.js.map +1 -1
- package/dist/tools/registry/misc-tools.js +1 -2
- package/dist/tools/registry/misc-tools.js.map +1 -1
- package/dist/tools/registry/search-tools.js +1 -1
- package/dist/tools/registry/search-tools.js.map +1 -1
- package/dist/tools/registry/text-editor-tools.js +1 -1
- package/dist/tools/registry/text-editor-tools.js.map +1 -1
- package/dist/tools/registry/todo-tools.js +37 -5
- package/dist/tools/registry/todo-tools.js.map +1 -1
- package/dist/tools/registry/tool-registry.js +5 -4
- package/dist/tools/registry/tool-registry.js.map +1 -1
- package/dist/tools/registry/web-tools.d.ts +1 -1
- package/dist/tools/registry/web-tools.js +28 -8
- package/dist/tools/registry/web-tools.js.map +1 -1
- package/dist/tools/text-editor.d.ts +1 -1
- package/dist/tools/text-editor.js +23 -5
- package/dist/tools/text-editor.js.map +1 -1
- package/dist/tools/web-search.d.ts +52 -37
- package/dist/tools/web-search.js +368 -163
- package/dist/tools/web-search.js.map +1 -1
- package/dist/ui/components/ChatInterface.d.ts +1 -1
- package/dist/utils/head-tail-truncation.d.ts +34 -0
- package/dist/utils/head-tail-truncation.js +98 -0
- package/dist/utils/head-tail-truncation.js.map +1 -0
- package/dist/utils/sanitize.d.ts +5 -0
- package/dist/utils/sanitize.js +19 -0
- package/dist/utils/sanitize.js.map +1 -1
- package/dist/utils/settings-manager.js +4 -4
- package/dist/utils/settings-manager.js.map +1 -1
- package/package.json +3 -1
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: playwright
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Browser automation, end-to-end testing, web scraping, form filling, screenshots, and PDF generation
|
|
5
|
+
author: Code Buddy
|
|
6
|
+
tags: browser, automation, testing, scraping, e2e, screenshots, pdf
|
|
7
|
+
env:
|
|
8
|
+
PLAYWRIGHT_BROWSERS_PATH: ""
|
|
9
|
+
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: ""
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Playwright Browser Automation
|
|
13
|
+
|
|
14
|
+
Playwright is a powerful browser automation framework that supports Chromium, Firefox, and WebKit. It enables reliable end-to-end testing, web scraping, form automation, screenshot capture, and PDF generation with a modern async API.
|
|
15
|
+
|
|
16
|
+
## Direct Control (CLI / API / Scripting)
|
|
17
|
+
|
|
18
|
+
### Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Install Playwright
|
|
22
|
+
npm install -D @playwright/test playwright
|
|
23
|
+
|
|
24
|
+
# Install browsers (Chromium, Firefox, WebKit)
|
|
25
|
+
npx playwright install
|
|
26
|
+
|
|
27
|
+
# Install specific browser only
|
|
28
|
+
npx playwright install chromium
|
|
29
|
+
|
|
30
|
+
# Install with system dependencies
|
|
31
|
+
npx playwright install --with-deps
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Node.js API Examples
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
const { chromium, firefox, webkit } = require('playwright');
|
|
38
|
+
|
|
39
|
+
// Basic page navigation and scraping
|
|
40
|
+
async function scrapePage(url) {
|
|
41
|
+
const browser = await chromium.launch({ headless: true });
|
|
42
|
+
const context = await browser.newContext();
|
|
43
|
+
const page = await context.newPage();
|
|
44
|
+
|
|
45
|
+
await page.goto(url);
|
|
46
|
+
await page.waitForLoadState('networkidle');
|
|
47
|
+
|
|
48
|
+
// Extract data
|
|
49
|
+
const title = await page.title();
|
|
50
|
+
const content = await page.textContent('body');
|
|
51
|
+
const links = await page.$$eval('a', anchors =>
|
|
52
|
+
anchors.map(a => ({ text: a.textContent, href: a.href }))
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
await browser.close();
|
|
56
|
+
return { title, content, links };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Form filling and submission
|
|
60
|
+
async function fillForm(url, formData) {
|
|
61
|
+
const browser = await chromium.launch({ headless: false });
|
|
62
|
+
const page = await browser.newPage();
|
|
63
|
+
|
|
64
|
+
await page.goto(url);
|
|
65
|
+
|
|
66
|
+
// Fill form fields
|
|
67
|
+
await page.fill('input[name="username"]', formData.username);
|
|
68
|
+
await page.fill('input[name="email"]', formData.email);
|
|
69
|
+
await page.fill('textarea[name="message"]', formData.message);
|
|
70
|
+
|
|
71
|
+
// Select dropdown
|
|
72
|
+
await page.selectOption('select[name="country"]', formData.country);
|
|
73
|
+
|
|
74
|
+
// Check checkbox
|
|
75
|
+
await page.check('input[type="checkbox"][name="terms"]');
|
|
76
|
+
|
|
77
|
+
// Click submit and wait for navigation
|
|
78
|
+
await Promise.all([
|
|
79
|
+
page.waitForNavigation(),
|
|
80
|
+
page.click('button[type="submit"]')
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
// Verify success
|
|
84
|
+
const successMessage = await page.textContent('.success-message');
|
|
85
|
+
await browser.close();
|
|
86
|
+
return successMessage;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Screenshot capture
|
|
90
|
+
async function captureScreenshot(url, outputPath, options = {}) {
|
|
91
|
+
const browser = await chromium.launch();
|
|
92
|
+
const page = await browser.newPage({
|
|
93
|
+
viewport: { width: 1920, height: 1080 }
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await page.goto(url);
|
|
97
|
+
await page.waitForLoadState('networkidle');
|
|
98
|
+
|
|
99
|
+
// Full page screenshot
|
|
100
|
+
await page.screenshot({
|
|
101
|
+
path: outputPath,
|
|
102
|
+
fullPage: options.fullPage || false,
|
|
103
|
+
type: options.type || 'png'
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Element screenshot
|
|
107
|
+
if (options.selector) {
|
|
108
|
+
const element = await page.$(options.selector);
|
|
109
|
+
await element.screenshot({ path: outputPath });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
await browser.close();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// PDF generation
|
|
116
|
+
async function generatePDF(url, outputPath, options = {}) {
|
|
117
|
+
const browser = await chromium.launch();
|
|
118
|
+
const page = await browser.newPage();
|
|
119
|
+
|
|
120
|
+
await page.goto(url);
|
|
121
|
+
await page.waitForLoadState('networkidle');
|
|
122
|
+
|
|
123
|
+
await page.pdf({
|
|
124
|
+
path: outputPath,
|
|
125
|
+
format: options.format || 'A4',
|
|
126
|
+
printBackground: true,
|
|
127
|
+
margin: options.margin || { top: '1cm', bottom: '1cm', left: '1cm', right: '1cm' }
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
await browser.close();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Handle authentication
|
|
134
|
+
async function loginAndNavigate(loginUrl, targetUrl, credentials) {
|
|
135
|
+
const browser = await chromium.launch();
|
|
136
|
+
const context = await browser.newContext();
|
|
137
|
+
const page = await context.newPage();
|
|
138
|
+
|
|
139
|
+
// Login
|
|
140
|
+
await page.goto(loginUrl);
|
|
141
|
+
await page.fill('#username', credentials.username);
|
|
142
|
+
await page.fill('#password', credentials.password);
|
|
143
|
+
await page.click('button[type="submit"]');
|
|
144
|
+
|
|
145
|
+
// Wait for login to complete
|
|
146
|
+
await page.waitForURL(/dashboard|home/);
|
|
147
|
+
|
|
148
|
+
// Save authentication state
|
|
149
|
+
await context.storageState({ path: 'auth.json' });
|
|
150
|
+
|
|
151
|
+
// Navigate to target with auth
|
|
152
|
+
await page.goto(targetUrl);
|
|
153
|
+
const data = await page.evaluate(() => document.body.innerText);
|
|
154
|
+
|
|
155
|
+
await browser.close();
|
|
156
|
+
return data;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Advanced: Intercept network requests
|
|
160
|
+
async function interceptRequests(url) {
|
|
161
|
+
const browser = await chromium.launch();
|
|
162
|
+
const page = await browser.newPage();
|
|
163
|
+
|
|
164
|
+
// Block images and stylesheets for faster loading
|
|
165
|
+
await page.route('**/*', route => {
|
|
166
|
+
const type = route.request().resourceType();
|
|
167
|
+
if (type === 'image' || type === 'stylesheet') {
|
|
168
|
+
route.abort();
|
|
169
|
+
} else {
|
|
170
|
+
route.continue();
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Listen to API responses
|
|
175
|
+
const apiData = [];
|
|
176
|
+
page.on('response', async response => {
|
|
177
|
+
if (response.url().includes('/api/')) {
|
|
178
|
+
apiData.push({
|
|
179
|
+
url: response.url(),
|
|
180
|
+
status: response.status(),
|
|
181
|
+
body: await response.json().catch(() => null)
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await page.goto(url);
|
|
187
|
+
await page.waitForTimeout(3000);
|
|
188
|
+
|
|
189
|
+
await browser.close();
|
|
190
|
+
return apiData;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Multi-browser testing
|
|
194
|
+
async function testAcrossBrowsers(url, testFn) {
|
|
195
|
+
const browsers = [chromium, firefox, webkit];
|
|
196
|
+
const results = {};
|
|
197
|
+
|
|
198
|
+
for (const browserType of browsers) {
|
|
199
|
+
const browser = await browserType.launch();
|
|
200
|
+
const page = await browser.newPage();
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
await page.goto(url);
|
|
204
|
+
results[browserType.name()] = await testFn(page);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
results[browserType.name()] = { error: error.message };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
await browser.close();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return results;
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Python API Examples
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from playwright.sync_api import sync_playwright
|
|
220
|
+
import json
|
|
221
|
+
|
|
222
|
+
# Basic scraping
|
|
223
|
+
def scrape_page(url):
|
|
224
|
+
with sync_playwright() as p:
|
|
225
|
+
browser = p.chromium.launch(headless=True)
|
|
226
|
+
page = browser.new_page()
|
|
227
|
+
page.goto(url)
|
|
228
|
+
|
|
229
|
+
title = page.title()
|
|
230
|
+
content = page.inner_text('body')
|
|
231
|
+
|
|
232
|
+
browser.close()
|
|
233
|
+
return {'title': title, 'content': content}
|
|
234
|
+
|
|
235
|
+
# Form automation
|
|
236
|
+
def automate_form(url, form_data):
|
|
237
|
+
with sync_playwright() as p:
|
|
238
|
+
browser = p.chromium.launch(headless=False)
|
|
239
|
+
page = browser.new_page()
|
|
240
|
+
page.goto(url)
|
|
241
|
+
|
|
242
|
+
page.fill('input[name="email"]', form_data['email'])
|
|
243
|
+
page.fill('input[name="password"]', form_data['password'])
|
|
244
|
+
page.click('button[type="submit"]')
|
|
245
|
+
|
|
246
|
+
page.wait_for_load_state('networkidle')
|
|
247
|
+
result = page.text_content('.result')
|
|
248
|
+
|
|
249
|
+
browser.close()
|
|
250
|
+
return result
|
|
251
|
+
|
|
252
|
+
# Screenshot with mobile viewport
|
|
253
|
+
def mobile_screenshot(url, output):
|
|
254
|
+
with sync_playwright() as p:
|
|
255
|
+
browser = p.chromium.launch()
|
|
256
|
+
context = browser.new_context(
|
|
257
|
+
viewport={'width': 375, 'height': 667},
|
|
258
|
+
user_agent='Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)'
|
|
259
|
+
)
|
|
260
|
+
page = context.new_page()
|
|
261
|
+
page.goto(url)
|
|
262
|
+
page.screenshot(path=output, full_page=True)
|
|
263
|
+
browser.close()
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### CLI Commands
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Run Playwright tests
|
|
270
|
+
npx playwright test
|
|
271
|
+
|
|
272
|
+
# Run specific test file
|
|
273
|
+
npx playwright test tests/login.spec.ts
|
|
274
|
+
|
|
275
|
+
# Run tests in headed mode (see browser)
|
|
276
|
+
npx playwright test --headed
|
|
277
|
+
|
|
278
|
+
# Run tests in specific browser
|
|
279
|
+
npx playwright test --project=firefox
|
|
280
|
+
|
|
281
|
+
# Debug tests with Playwright Inspector
|
|
282
|
+
npx playwright test --debug
|
|
283
|
+
|
|
284
|
+
# Generate code from browser interactions (codegen)
|
|
285
|
+
npx playwright codegen https://example.com
|
|
286
|
+
|
|
287
|
+
# Generate code with custom viewport
|
|
288
|
+
npx playwright codegen --viewport-size=1280,720 https://example.com
|
|
289
|
+
|
|
290
|
+
# Show trace viewer
|
|
291
|
+
npx playwright show-trace trace.zip
|
|
292
|
+
|
|
293
|
+
# Show HTML report
|
|
294
|
+
npx playwright show-report
|
|
295
|
+
|
|
296
|
+
# Install system dependencies
|
|
297
|
+
npx playwright install-deps
|
|
298
|
+
|
|
299
|
+
# Clear browser cache
|
|
300
|
+
rm -rf ~/.cache/ms-playwright
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## MCP Server Integration
|
|
304
|
+
|
|
305
|
+
Add to `.codebuddy/mcp.json`:
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{
|
|
309
|
+
"mcpServers": {
|
|
310
|
+
"playwright": {
|
|
311
|
+
"command": "npx",
|
|
312
|
+
"args": [
|
|
313
|
+
"-y",
|
|
314
|
+
"@executeautomation/playwright-mcp-server"
|
|
315
|
+
],
|
|
316
|
+
"env": {
|
|
317
|
+
"PLAYWRIGHT_BROWSERS_PATH": "~/.cache/ms-playwright"
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Available MCP Tools
|
|
325
|
+
|
|
326
|
+
**From microsoft/playwright-mcp:**
|
|
327
|
+
- `playwright_navigate` - Navigate to URL
|
|
328
|
+
- `playwright_click` - Click element by selector
|
|
329
|
+
- `playwright_fill` - Fill input field
|
|
330
|
+
- `playwright_screenshot` - Capture screenshot
|
|
331
|
+
- `playwright_evaluate` - Execute JavaScript in page context
|
|
332
|
+
- `playwright_close` - Close browser/page
|
|
333
|
+
|
|
334
|
+
**From executeautomation/playwright-mcp-server:**
|
|
335
|
+
- `playwright_launch_browser` - Launch browser instance
|
|
336
|
+
- `playwright_goto` - Navigate to URL
|
|
337
|
+
- `playwright_click_element` - Click element with wait
|
|
338
|
+
- `playwright_fill_form` - Fill form fields
|
|
339
|
+
- `playwright_get_text` - Extract text content
|
|
340
|
+
- `playwright_wait_for_selector` - Wait for element
|
|
341
|
+
- `playwright_screenshot_element` - Screenshot specific element
|
|
342
|
+
- `playwright_pdf` - Generate PDF
|
|
343
|
+
- `playwright_execute_script` - Run JavaScript
|
|
344
|
+
|
|
345
|
+
## Common Workflows
|
|
346
|
+
|
|
347
|
+
### 1. Web Scraping: Extract Product Information
|
|
348
|
+
|
|
349
|
+
```javascript
|
|
350
|
+
// Step 1: Launch browser and navigate
|
|
351
|
+
const browser = await chromium.launch({ headless: true });
|
|
352
|
+
const page = await browser.newPage();
|
|
353
|
+
await page.goto('https://example-shop.com/products');
|
|
354
|
+
|
|
355
|
+
// Step 2: Wait for products to load
|
|
356
|
+
await page.waitForSelector('.product-card', { timeout: 10000 });
|
|
357
|
+
|
|
358
|
+
// Step 3: Extract product data
|
|
359
|
+
const products = await page.$$eval('.product-card', cards => {
|
|
360
|
+
return cards.map(card => ({
|
|
361
|
+
name: card.querySelector('.product-name')?.textContent,
|
|
362
|
+
price: card.querySelector('.product-price')?.textContent,
|
|
363
|
+
image: card.querySelector('img')?.src,
|
|
364
|
+
url: card.querySelector('a')?.href
|
|
365
|
+
}));
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
// Step 4: Paginate through results
|
|
369
|
+
let allProducts = [...products];
|
|
370
|
+
while (await page.$('.next-page')) {
|
|
371
|
+
await page.click('.next-page');
|
|
372
|
+
await page.waitForLoadState('networkidle');
|
|
373
|
+
const moreProducts = await page.$$eval('.product-card', /* ... */);
|
|
374
|
+
allProducts = [...allProducts, ...moreProducts];
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Step 5: Save results and cleanup
|
|
378
|
+
await fs.writeFile('products.json', JSON.stringify(allProducts, null, 2));
|
|
379
|
+
await browser.close();
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### 2. E2E Testing: Login Flow Verification
|
|
383
|
+
|
|
384
|
+
```javascript
|
|
385
|
+
// Step 1: Setup test with authentication state
|
|
386
|
+
const browser = await chromium.launch();
|
|
387
|
+
const context = await browser.newContext();
|
|
388
|
+
const page = await context.newPage();
|
|
389
|
+
|
|
390
|
+
// Step 2: Navigate to login page
|
|
391
|
+
await page.goto('https://app.example.com/login');
|
|
392
|
+
await page.fill('#email', 'test@example.com');
|
|
393
|
+
await page.fill('#password', 'SecurePassword123');
|
|
394
|
+
|
|
395
|
+
// Step 3: Submit and verify redirect
|
|
396
|
+
await Promise.all([
|
|
397
|
+
page.waitForNavigation({ url: /dashboard/ }),
|
|
398
|
+
page.click('button[type="submit"]')
|
|
399
|
+
]);
|
|
400
|
+
|
|
401
|
+
// Step 4: Verify authenticated state
|
|
402
|
+
await expect(page.locator('.user-profile')).toBeVisible();
|
|
403
|
+
const username = await page.textContent('.user-name');
|
|
404
|
+
expect(username).toBe('Test User');
|
|
405
|
+
|
|
406
|
+
// Step 5: Test logout
|
|
407
|
+
await page.click('.logout-button');
|
|
408
|
+
await page.waitForURL(/login/);
|
|
409
|
+
await browser.close();
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### 3. Form Automation: Multi-Step Checkout
|
|
413
|
+
|
|
414
|
+
```javascript
|
|
415
|
+
// Step 1: Navigate to checkout
|
|
416
|
+
const browser = await chromium.launch({ headless: false });
|
|
417
|
+
const page = await browser.newPage();
|
|
418
|
+
await page.goto('https://shop.example.com/checkout');
|
|
419
|
+
|
|
420
|
+
// Step 2: Fill shipping information
|
|
421
|
+
await page.fill('#firstName', 'John');
|
|
422
|
+
await page.fill('#lastName', 'Doe');
|
|
423
|
+
await page.fill('#address', '123 Main St');
|
|
424
|
+
await page.fill('#city', 'San Francisco');
|
|
425
|
+
await page.selectOption('#state', 'CA');
|
|
426
|
+
await page.fill('#zip', '94105');
|
|
427
|
+
await page.click('button.continue-to-payment');
|
|
428
|
+
|
|
429
|
+
// Step 3: Fill payment details
|
|
430
|
+
await page.waitForSelector('#card-number');
|
|
431
|
+
await page.fill('#card-number', '4111111111111111');
|
|
432
|
+
await page.fill('#card-expiry', '12/25');
|
|
433
|
+
await page.fill('#card-cvc', '123');
|
|
434
|
+
|
|
435
|
+
// Step 4: Review and submit order
|
|
436
|
+
await page.click('button.continue-to-review');
|
|
437
|
+
await page.waitForSelector('.order-summary');
|
|
438
|
+
const total = await page.textContent('.total-amount');
|
|
439
|
+
console.log(`Order total: ${total}`);
|
|
440
|
+
|
|
441
|
+
// Step 5: Complete purchase
|
|
442
|
+
await page.click('button.place-order');
|
|
443
|
+
await page.waitForSelector('.confirmation-number');
|
|
444
|
+
const confirmationNumber = await page.textContent('.confirmation-number');
|
|
445
|
+
await browser.close();
|
|
446
|
+
return confirmationNumber;
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### 4. Visual Testing: Screenshot Comparison
|
|
450
|
+
|
|
451
|
+
```javascript
|
|
452
|
+
// Step 1: Setup baseline screenshot
|
|
453
|
+
const browser = await chromium.launch();
|
|
454
|
+
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
|
|
455
|
+
await page.goto('https://example.com');
|
|
456
|
+
await page.screenshot({ path: 'baseline.png', fullPage: true });
|
|
457
|
+
|
|
458
|
+
// Step 2: Make changes (e.g., deploy new version)
|
|
459
|
+
// ... deployment happens ...
|
|
460
|
+
|
|
461
|
+
// Step 3: Capture new screenshot
|
|
462
|
+
await page.goto('https://example.com');
|
|
463
|
+
await page.screenshot({ path: 'current.png', fullPage: true });
|
|
464
|
+
|
|
465
|
+
// Step 4: Compare screenshots (using pixelmatch or similar)
|
|
466
|
+
const baseline = await fs.readFile('baseline.png');
|
|
467
|
+
const current = await fs.readFile('current.png');
|
|
468
|
+
const diff = compareImages(baseline, current);
|
|
469
|
+
|
|
470
|
+
// Step 5: Report differences
|
|
471
|
+
if (diff.pixelsDifferent > 100) {
|
|
472
|
+
console.error(`Visual regression detected: ${diff.pixelsDifferent} pixels differ`);
|
|
473
|
+
await fs.writeFile('diff.png', diff.image);
|
|
474
|
+
}
|
|
475
|
+
await browser.close();
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### 5. Monitoring: Check Website Availability
|
|
479
|
+
|
|
480
|
+
```javascript
|
|
481
|
+
// Step 1: Setup monitoring function
|
|
482
|
+
async function monitorWebsite(url, checks) {
|
|
483
|
+
const browser = await chromium.launch({ headless: true });
|
|
484
|
+
const page = await browser.newPage();
|
|
485
|
+
const results = { url, timestamp: new Date(), checks: {} };
|
|
486
|
+
|
|
487
|
+
// Step 2: Navigate and measure load time
|
|
488
|
+
const startTime = Date.now();
|
|
489
|
+
const response = await page.goto(url, { waitUntil: 'networkidle' });
|
|
490
|
+
results.loadTime = Date.now() - startTime;
|
|
491
|
+
results.statusCode = response.status();
|
|
492
|
+
|
|
493
|
+
// Step 3: Verify expected elements
|
|
494
|
+
for (const check of checks) {
|
|
495
|
+
const element = await page.$(check.selector);
|
|
496
|
+
results.checks[check.name] = {
|
|
497
|
+
found: !!element,
|
|
498
|
+
text: element ? await element.textContent() : null
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Step 4: Check for errors in console
|
|
503
|
+
const logs = [];
|
|
504
|
+
page.on('console', msg => logs.push({ type: msg.type(), text: msg.text() }));
|
|
505
|
+
await page.waitForTimeout(2000);
|
|
506
|
+
results.consoleLogs = logs.filter(l => l.type === 'error');
|
|
507
|
+
|
|
508
|
+
// Step 5: Cleanup and return results
|
|
509
|
+
await browser.close();
|
|
510
|
+
return results;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Usage
|
|
514
|
+
const checks = [
|
|
515
|
+
{ name: 'header', selector: 'header.main-nav' },
|
|
516
|
+
{ name: 'content', selector: 'main.content' },
|
|
517
|
+
{ name: 'footer', selector: 'footer' }
|
|
518
|
+
];
|
|
519
|
+
const status = await monitorWebsite('https://example.com', checks);
|
|
520
|
+
```
|