@phuetz/code-buddy 0.1.0 → 0.1.2
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 +4 -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 +46 -14
- 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/index.d.ts +3 -3
- package/dist/agent/index.js +3 -3
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/isolation/agent-workspace.d.ts +1 -0
- package/dist/agent/isolation/agent-workspace.js +10 -0
- package/dist/agent/isolation/agent-workspace.js.map +1 -1
- 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/index.d.ts +9 -8
- package/dist/agent/specialized/index.js +16 -8
- package/dist/agent/specialized/index.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/browser/controller.js +8 -4
- package/dist/browser/controller.js.map +1 -1
- package/dist/browser-automation/browser-manager.js +8 -1
- package/dist/browser-automation/browser-manager.js.map +1 -1
- 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/codebuddy/client.js +70 -11
- package/dist/codebuddy/client.js.map +1 -1
- package/dist/codebuddy/tools.d.ts +1 -7
- package/dist/codebuddy/tools.js +2 -30
- package/dist/codebuddy/tools.js.map +1 -1
- package/dist/commands/cli/daemon-commands.d.ts +14 -0
- package/dist/commands/cli/daemon-commands.js +166 -0
- package/dist/commands/cli/daemon-commands.js.map +1 -0
- package/dist/commands/cli/speak-command.d.ts +10 -0
- package/dist/commands/cli/speak-command.js +97 -0
- package/dist/commands/cli/speak-command.js.map +1 -0
- package/dist/commands/cli/utility-commands.d.ts +10 -0
- package/dist/commands/cli/utility-commands.js +88 -0
- package/dist/commands/cli/utility-commands.js.map +1 -0
- 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/vibe-handlers.js +0 -1
- package/dist/commands/handlers/vibe-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/index.d.ts +8 -7
- package/dist/commands/index.js +10 -8
- package/dist/commands/index.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/hot-reload/watcher.js +4 -4
- package/dist/config/hot-reload/watcher.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/context/index.d.ts +12 -12
- package/dist/context/index.js +25 -12
- package/dist/context/index.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/errors/index.d.ts +4 -4
- package/dist/errors/index.js +8 -4
- package/dist/errors/index.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/index.d.ts +4 -4
- package/dist/hooks/index.js +4 -4
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/use-input-handler.d.ts +1 -1
- package/dist/index.js +20 -330
- 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.d.ts +9 -0
- package/dist/integrations/json-rpc/server.js +43 -13
- 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/integrations/notification-integrations.d.ts +1 -0
- package/dist/integrations/notification-integrations.js +6 -1
- package/dist/integrations/notification-integrations.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/memory/index.d.ts +2 -2
- package/dist/memory/index.js +2 -2
- package/dist/memory/index.js.map +1 -1
- 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/conflict-detection.js +2 -1
- package/dist/plugins/conflict-detection.js.map +1 -1
- package/dist/plugins/index.d.ts +3 -3
- package/dist/plugins/index.js +3 -3
- package/dist/plugins/index.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 +28 -8
- 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 +488 -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/index.d.ts +7 -5
- package/dist/security/index.js +8 -7
- package/dist/security/index.js.map +1 -1
- 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/auth/index.d.ts +2 -2
- package/dist/server/auth/index.js +2 -2
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/middleware/index.d.ts +5 -5
- package/dist/server/middleware/index.js +5 -5
- package/dist/server/middleware/index.js.map +1 -1
- package/dist/server/middleware/rate-limit.js +15 -3
- package/dist/server/middleware/rate-limit.js.map +1 -1
- package/dist/server/websocket/handler.js +54 -6
- 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/hooks/default-hooks.d.ts +1 -1
- package/dist/tools/hooks/default-hooks.js +2 -1
- package/dist/tools/hooks/default-hooks.js.map +1 -1
- package/dist/tools/index.d.ts +10 -10
- package/dist/tools/index.js +11 -11
- package/dist/tools/index.js.map +1 -1
- 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/types/errors.d.ts +1 -1
- package/dist/types/errors.js +2 -8
- package/dist/types/errors.js.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/index.js +1 -2
- package/dist/types/index.js.map +1 -1
- package/dist/ui/components/ChatInterface.d.ts +1 -1
- package/dist/ui/index.d.ts +17 -21
- package/dist/ui/index.js +25 -22
- package/dist/ui/index.js.map +1 -1
- package/dist/utils/config-validation/schema.d.ts +15 -15
- 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/logger.js +3 -9
- package/dist/utils/logger.js.map +1 -1
- 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/dist/workflows/index.d.ts +4 -279
- package/dist/workflows/index.js +8 -822
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/state-manager.d.ts +77 -0
- package/dist/workflows/state-manager.js +198 -0
- package/dist/workflows/state-manager.js.map +1 -0
- package/dist/workflows/step-manager.d.ts +39 -0
- package/dist/workflows/step-manager.js +196 -0
- package/dist/workflows/step-manager.js.map +1 -0
- package/dist/workflows/types.d.ts +87 -0
- package/dist/workflows/types.js +5 -0
- package/dist/workflows/types.js.map +1 -0
- package/dist/workflows/workflow-engine.d.ts +34 -0
- package/dist/workflows/workflow-engine.js +354 -0
- package/dist/workflows/workflow-engine.js.map +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,708 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: puppeteer
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: Chrome/Chromium automation for headless browsing, web scraping, PDF generation, and screenshots
|
|
5
|
+
author: Code Buddy
|
|
6
|
+
tags: chrome, automation, scraping, headless, pdf, screenshots, debugging
|
|
7
|
+
env:
|
|
8
|
+
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: ""
|
|
9
|
+
PUPPETEER_EXECUTABLE_PATH: ""
|
|
10
|
+
PUPPETEER_CACHE_DIR: ""
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Puppeteer Chrome Automation
|
|
14
|
+
|
|
15
|
+
Puppeteer is a Node.js library that provides a high-level API to control Chrome or Chromium over the DevTools Protocol. It's excellent for web scraping, automated testing, screenshot capture, PDF generation, and debugging web applications.
|
|
16
|
+
|
|
17
|
+
## Direct Control (CLI / API / Scripting)
|
|
18
|
+
|
|
19
|
+
### Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Install Puppeteer (includes Chromium)
|
|
23
|
+
npm install puppeteer
|
|
24
|
+
|
|
25
|
+
# Install puppeteer-core (no Chromium download)
|
|
26
|
+
npm install puppeteer-core
|
|
27
|
+
|
|
28
|
+
# Use system Chrome
|
|
29
|
+
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true npm install puppeteer-core
|
|
30
|
+
|
|
31
|
+
# Install with specific Chrome version
|
|
32
|
+
PUPPETEER_CHROMIUM_REVISION=1095492 npm install puppeteer
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Node.js API Examples
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
const puppeteer = require('puppeteer');
|
|
39
|
+
const fs = require('fs').promises;
|
|
40
|
+
|
|
41
|
+
// Basic page scraping
|
|
42
|
+
async function scrapePage(url) {
|
|
43
|
+
const browser = await puppeteer.launch({
|
|
44
|
+
headless: 'new', // Use new headless mode
|
|
45
|
+
args: ['--no-sandbox', '--disable-setuid-sandbox']
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const page = await browser.newPage();
|
|
49
|
+
await page.goto(url, { waitUntil: 'networkidle2' });
|
|
50
|
+
|
|
51
|
+
// Extract data
|
|
52
|
+
const data = await page.evaluate(() => {
|
|
53
|
+
return {
|
|
54
|
+
title: document.title,
|
|
55
|
+
description: document.querySelector('meta[name="description"]')?.content,
|
|
56
|
+
headings: Array.from(document.querySelectorAll('h1, h2, h3')).map(h => ({
|
|
57
|
+
level: h.tagName,
|
|
58
|
+
text: h.textContent.trim()
|
|
59
|
+
})),
|
|
60
|
+
links: Array.from(document.querySelectorAll('a')).map(a => ({
|
|
61
|
+
text: a.textContent.trim(),
|
|
62
|
+
href: a.href
|
|
63
|
+
}))
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
await browser.close();
|
|
68
|
+
return data;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Advanced scraping with infinite scroll
|
|
72
|
+
async function scrapeInfiniteScroll(url, scrollLimit = 10) {
|
|
73
|
+
const browser = await puppeteer.launch({ headless: 'new' });
|
|
74
|
+
const page = await browser.newPage();
|
|
75
|
+
await page.goto(url, { waitUntil: 'networkidle2' });
|
|
76
|
+
|
|
77
|
+
let previousHeight;
|
|
78
|
+
let scrollCount = 0;
|
|
79
|
+
|
|
80
|
+
while (scrollCount < scrollLimit) {
|
|
81
|
+
previousHeight = await page.evaluate('document.body.scrollHeight');
|
|
82
|
+
|
|
83
|
+
// Scroll to bottom
|
|
84
|
+
await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
|
|
85
|
+
await page.waitForTimeout(2000);
|
|
86
|
+
|
|
87
|
+
const newHeight = await page.evaluate('document.body.scrollHeight');
|
|
88
|
+
if (newHeight === previousHeight) break;
|
|
89
|
+
scrollCount++;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Extract all items
|
|
93
|
+
const items = await page.evaluate(() => {
|
|
94
|
+
return Array.from(document.querySelectorAll('.item')).map(item => ({
|
|
95
|
+
title: item.querySelector('.title')?.textContent,
|
|
96
|
+
image: item.querySelector('img')?.src,
|
|
97
|
+
price: item.querySelector('.price')?.textContent
|
|
98
|
+
}));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await browser.close();
|
|
102
|
+
return items;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Form automation with validation
|
|
106
|
+
async function fillAndSubmitForm(url, formData) {
|
|
107
|
+
const browser = await puppeteer.launch({ headless: false, slowMo: 50 });
|
|
108
|
+
const page = await browser.newPage();
|
|
109
|
+
await page.goto(url);
|
|
110
|
+
|
|
111
|
+
// Wait for form to be ready
|
|
112
|
+
await page.waitForSelector('form');
|
|
113
|
+
|
|
114
|
+
// Fill text inputs
|
|
115
|
+
await page.type('#name', formData.name, { delay: 100 });
|
|
116
|
+
await page.type('#email', formData.email);
|
|
117
|
+
await page.type('#phone', formData.phone);
|
|
118
|
+
|
|
119
|
+
// Select dropdown
|
|
120
|
+
await page.select('#country', formData.country);
|
|
121
|
+
|
|
122
|
+
// Radio buttons
|
|
123
|
+
await page.click(`input[name="gender"][value="${formData.gender}"]`);
|
|
124
|
+
|
|
125
|
+
// Checkboxes
|
|
126
|
+
if (formData.newsletter) {
|
|
127
|
+
await page.click('#newsletter');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// File upload
|
|
131
|
+
if (formData.resume) {
|
|
132
|
+
const fileInput = await page.$('#resume');
|
|
133
|
+
await fileInput.uploadFile(formData.resume);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Submit and wait for response
|
|
137
|
+
await Promise.all([
|
|
138
|
+
page.waitForNavigation({ waitUntil: 'networkidle2' }),
|
|
139
|
+
page.click('button[type="submit"]')
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
// Check for validation errors
|
|
143
|
+
const errors = await page.$$eval('.error', els => els.map(e => e.textContent));
|
|
144
|
+
const success = await page.$('.success-message');
|
|
145
|
+
|
|
146
|
+
await browser.close();
|
|
147
|
+
return { success: !!success, errors };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Screenshot with full page and element capture
|
|
151
|
+
async function captureScreenshots(url, outputDir) {
|
|
152
|
+
const browser = await puppeteer.launch();
|
|
153
|
+
const page = await browser.newPage();
|
|
154
|
+
|
|
155
|
+
// Set viewport for consistent screenshots
|
|
156
|
+
await page.setViewport({ width: 1920, height: 1080 });
|
|
157
|
+
await page.goto(url, { waitUntil: 'networkidle2' });
|
|
158
|
+
|
|
159
|
+
// Full page screenshot
|
|
160
|
+
await page.screenshot({
|
|
161
|
+
path: `${outputDir}/full-page.png`,
|
|
162
|
+
fullPage: true
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Viewport screenshot
|
|
166
|
+
await page.screenshot({
|
|
167
|
+
path: `${outputDir}/viewport.png`
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Element screenshot
|
|
171
|
+
const element = await page.$('.hero-section');
|
|
172
|
+
if (element) {
|
|
173
|
+
await element.screenshot({
|
|
174
|
+
path: `${outputDir}/hero.png`
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Screenshot with different device emulation
|
|
179
|
+
await page.emulate(puppeteer.KnownDevices['iPhone 13 Pro']);
|
|
180
|
+
await page.screenshot({
|
|
181
|
+
path: `${outputDir}/mobile.png`,
|
|
182
|
+
fullPage: true
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
await browser.close();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// PDF generation with custom options
|
|
189
|
+
async function generatePDF(url, outputPath, options = {}) {
|
|
190
|
+
const browser = await puppeteer.launch();
|
|
191
|
+
const page = await browser.newPage();
|
|
192
|
+
await page.goto(url, { waitUntil: 'networkidle2' });
|
|
193
|
+
|
|
194
|
+
// Remove unwanted elements before PDF
|
|
195
|
+
await page.evaluate(() => {
|
|
196
|
+
document.querySelectorAll('.no-print, .advertisement').forEach(el => el.remove());
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
await page.pdf({
|
|
200
|
+
path: outputPath,
|
|
201
|
+
format: options.format || 'A4',
|
|
202
|
+
printBackground: true,
|
|
203
|
+
margin: {
|
|
204
|
+
top: options.marginTop || '1cm',
|
|
205
|
+
right: options.marginRight || '1cm',
|
|
206
|
+
bottom: options.marginBottom || '1cm',
|
|
207
|
+
left: options.marginLeft || '1cm'
|
|
208
|
+
},
|
|
209
|
+
displayHeaderFooter: options.headerFooter || false,
|
|
210
|
+
headerTemplate: options.header || '',
|
|
211
|
+
footerTemplate: options.footer || '<div style="font-size:10px;text-align:center;width:100%;"><span class="pageNumber"></span></div>'
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
await browser.close();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Performance monitoring and metrics
|
|
218
|
+
async function measurePerformance(url) {
|
|
219
|
+
const browser = await puppeteer.launch();
|
|
220
|
+
const page = await browser.newPage();
|
|
221
|
+
|
|
222
|
+
// Enable performance metrics
|
|
223
|
+
await page.setCacheEnabled(false);
|
|
224
|
+
|
|
225
|
+
const startTime = Date.now();
|
|
226
|
+
await page.goto(url, { waitUntil: 'networkidle2' });
|
|
227
|
+
const loadTime = Date.now() - startTime;
|
|
228
|
+
|
|
229
|
+
// Get performance metrics
|
|
230
|
+
const metrics = await page.metrics();
|
|
231
|
+
const performanceTiming = JSON.parse(
|
|
232
|
+
await page.evaluate(() => JSON.stringify(window.performance.timing))
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
// Get resource timings
|
|
236
|
+
const resourceTimings = await page.evaluate(() => {
|
|
237
|
+
return performance.getEntriesByType('resource').map(r => ({
|
|
238
|
+
name: r.name,
|
|
239
|
+
duration: r.duration,
|
|
240
|
+
size: r.transferSize
|
|
241
|
+
}));
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Get Core Web Vitals
|
|
245
|
+
const vitals = await page.evaluate(() => {
|
|
246
|
+
return new Promise(resolve => {
|
|
247
|
+
const vitals = {};
|
|
248
|
+
new PerformanceObserver(list => {
|
|
249
|
+
for (const entry of list.getEntries()) {
|
|
250
|
+
if (entry.name === 'first-contentful-paint') {
|
|
251
|
+
vitals.FCP = entry.startTime;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}).observe({ entryTypes: ['paint'] });
|
|
255
|
+
|
|
256
|
+
setTimeout(() => resolve(vitals), 3000);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
await browser.close();
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
loadTime,
|
|
264
|
+
metrics,
|
|
265
|
+
performanceTiming,
|
|
266
|
+
resourceTimings,
|
|
267
|
+
vitals
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Request interception and modification
|
|
272
|
+
async function interceptRequests(url, rules) {
|
|
273
|
+
const browser = await puppeteer.launch();
|
|
274
|
+
const page = await browser.newPage();
|
|
275
|
+
|
|
276
|
+
await page.setRequestInterception(true);
|
|
277
|
+
|
|
278
|
+
page.on('request', request => {
|
|
279
|
+
const url = request.url();
|
|
280
|
+
const resourceType = request.resourceType();
|
|
281
|
+
|
|
282
|
+
// Block certain resources
|
|
283
|
+
if (rules.blockTypes?.includes(resourceType)) {
|
|
284
|
+
request.abort();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Block specific domains
|
|
289
|
+
if (rules.blockDomains?.some(domain => url.includes(domain))) {
|
|
290
|
+
request.abort();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Modify headers
|
|
295
|
+
if (rules.modifyHeaders) {
|
|
296
|
+
request.continue({
|
|
297
|
+
headers: { ...request.headers(), ...rules.modifyHeaders }
|
|
298
|
+
});
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
request.continue();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// Collect responses
|
|
306
|
+
const responses = [];
|
|
307
|
+
page.on('response', response => {
|
|
308
|
+
responses.push({
|
|
309
|
+
url: response.url(),
|
|
310
|
+
status: response.status(),
|
|
311
|
+
headers: response.headers()
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
await page.goto(url);
|
|
316
|
+
await page.waitForTimeout(3000);
|
|
317
|
+
|
|
318
|
+
await browser.close();
|
|
319
|
+
return responses;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Authentication and session management
|
|
323
|
+
async function authenticateAndScrape(loginUrl, targetUrl, credentials) {
|
|
324
|
+
const browser = await puppeteer.launch({ headless: 'new' });
|
|
325
|
+
const page = await browser.newPage();
|
|
326
|
+
|
|
327
|
+
// Login
|
|
328
|
+
await page.goto(loginUrl);
|
|
329
|
+
await page.type('#username', credentials.username);
|
|
330
|
+
await page.type('#password', credentials.password);
|
|
331
|
+
await Promise.all([
|
|
332
|
+
page.waitForNavigation(),
|
|
333
|
+
page.click('button[type="submit"]')
|
|
334
|
+
]);
|
|
335
|
+
|
|
336
|
+
// Save cookies
|
|
337
|
+
const cookies = await page.cookies();
|
|
338
|
+
await fs.writeFile('cookies.json', JSON.stringify(cookies));
|
|
339
|
+
|
|
340
|
+
// Navigate to target page with auth
|
|
341
|
+
await page.goto(targetUrl);
|
|
342
|
+
const data = await page.evaluate(() => document.body.textContent);
|
|
343
|
+
|
|
344
|
+
await browser.close();
|
|
345
|
+
return data;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Reuse saved session
|
|
349
|
+
async function reuseSession(url, cookiesPath) {
|
|
350
|
+
const browser = await puppeteer.launch();
|
|
351
|
+
const page = await browser.newPage();
|
|
352
|
+
|
|
353
|
+
// Load saved cookies
|
|
354
|
+
const cookiesString = await fs.readFile(cookiesPath);
|
|
355
|
+
const cookies = JSON.parse(cookiesString);
|
|
356
|
+
await page.setCookie(...cookies);
|
|
357
|
+
|
|
358
|
+
await page.goto(url);
|
|
359
|
+
const isAuthenticated = await page.$('.user-profile');
|
|
360
|
+
|
|
361
|
+
await browser.close();
|
|
362
|
+
return isAuthenticated;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Browser debugging and Chrome DevTools
|
|
366
|
+
async function debugPage(url) {
|
|
367
|
+
const browser = await puppeteer.launch({
|
|
368
|
+
headless: false,
|
|
369
|
+
devtools: true, // Open DevTools automatically
|
|
370
|
+
slowMo: 100 // Slow down by 100ms
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const page = await browser.newPage();
|
|
374
|
+
|
|
375
|
+
// Listen to console logs
|
|
376
|
+
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
|
|
377
|
+
|
|
378
|
+
// Listen to errors
|
|
379
|
+
page.on('pageerror', error => console.log('PAGE ERROR:', error.message));
|
|
380
|
+
|
|
381
|
+
// Listen to request failures
|
|
382
|
+
page.on('requestfailed', request => {
|
|
383
|
+
console.log('REQUEST FAILED:', request.url(), request.failure().errorText);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
await page.goto(url);
|
|
387
|
+
|
|
388
|
+
// Keep browser open for debugging
|
|
389
|
+
await new Promise(() => {});
|
|
390
|
+
}
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Python API (Pyppeteer) Examples
|
|
394
|
+
|
|
395
|
+
```python
|
|
396
|
+
import asyncio
|
|
397
|
+
from pyppeteer import launch
|
|
398
|
+
|
|
399
|
+
async def scrape_page(url):
|
|
400
|
+
browser = await launch(headless=True)
|
|
401
|
+
page = await browser.newPage()
|
|
402
|
+
await page.goto(url)
|
|
403
|
+
|
|
404
|
+
title = await page.title()
|
|
405
|
+
content = await page.evaluate('() => document.body.textContent')
|
|
406
|
+
|
|
407
|
+
await browser.close()
|
|
408
|
+
return {'title': title, 'content': content}
|
|
409
|
+
|
|
410
|
+
async def take_screenshot(url, output):
|
|
411
|
+
browser = await launch()
|
|
412
|
+
page = await browser.newPage()
|
|
413
|
+
await page.setViewport({'width': 1920, 'height': 1080})
|
|
414
|
+
await page.goto(url)
|
|
415
|
+
await page.screenshot({'path': output, 'fullPage': True})
|
|
416
|
+
await browser.close()
|
|
417
|
+
|
|
418
|
+
# Run async function
|
|
419
|
+
asyncio.run(scrape_page('https://example.com'))
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
### CLI Usage
|
|
423
|
+
|
|
424
|
+
```bash
|
|
425
|
+
# Launch Puppeteer with specific Chrome
|
|
426
|
+
PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome node script.js
|
|
427
|
+
|
|
428
|
+
# Set custom cache directory
|
|
429
|
+
PUPPETEER_CACHE_DIR=~/.cache/puppeteer npm install puppeteer
|
|
430
|
+
|
|
431
|
+
# Use system Chrome (skip download)
|
|
432
|
+
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true npm install puppeteer-core
|
|
433
|
+
|
|
434
|
+
# Debug script with verbose logging
|
|
435
|
+
DEBUG=puppeteer:* node script.js
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
## MCP Server Integration
|
|
439
|
+
|
|
440
|
+
Add to `.codebuddy/mcp.json`:
|
|
441
|
+
|
|
442
|
+
```json
|
|
443
|
+
{
|
|
444
|
+
"mcpServers": {
|
|
445
|
+
"puppeteer": {
|
|
446
|
+
"command": "npx",
|
|
447
|
+
"args": [
|
|
448
|
+
"-y",
|
|
449
|
+
"@nicholasoxford/puppeteer-mcp"
|
|
450
|
+
],
|
|
451
|
+
"env": {
|
|
452
|
+
"PUPPETEER_EXECUTABLE_PATH": "/usr/bin/google-chrome"
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Or use Anthropic's official server:
|
|
460
|
+
|
|
461
|
+
```json
|
|
462
|
+
{
|
|
463
|
+
"mcpServers": {
|
|
464
|
+
"puppeteer": {
|
|
465
|
+
"command": "npx",
|
|
466
|
+
"args": [
|
|
467
|
+
"-y",
|
|
468
|
+
"@anthropics/puppeteer-mcp"
|
|
469
|
+
]
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
### Available MCP Tools
|
|
476
|
+
|
|
477
|
+
**From @nicholasoxford/puppeteer-mcp:**
|
|
478
|
+
- `puppeteer_navigate` - Navigate to URL
|
|
479
|
+
- `puppeteer_screenshot` - Capture screenshot (full page or element)
|
|
480
|
+
- `puppeteer_click` - Click element by selector
|
|
481
|
+
- `puppeteer_fill` - Fill input field
|
|
482
|
+
- `puppeteer_select` - Select dropdown option
|
|
483
|
+
- `puppeteer_evaluate` - Execute JavaScript in page
|
|
484
|
+
- `puppeteer_pdf` - Generate PDF from page
|
|
485
|
+
|
|
486
|
+
**From @anthropics/puppeteer-mcp:**
|
|
487
|
+
- `puppeteer_launch` - Launch browser instance
|
|
488
|
+
- `puppeteer_goto` - Navigate to URL with options
|
|
489
|
+
- `puppeteer_click_element` - Click with wait and retry
|
|
490
|
+
- `puppeteer_type_text` - Type text with delay
|
|
491
|
+
- `puppeteer_get_content` - Extract page content
|
|
492
|
+
- `puppeteer_screenshot_full` - Full page screenshot
|
|
493
|
+
- `puppeteer_close_browser` - Close browser instance
|
|
494
|
+
|
|
495
|
+
## Common Workflows
|
|
496
|
+
|
|
497
|
+
### 1. E-Commerce Price Monitoring
|
|
498
|
+
|
|
499
|
+
```javascript
|
|
500
|
+
// Step 1: Launch browser and navigate to product page
|
|
501
|
+
const browser = await puppeteer.launch({ headless: 'new' });
|
|
502
|
+
const page = await browser.newPage();
|
|
503
|
+
await page.goto('https://shop.example.com/product/12345');
|
|
504
|
+
|
|
505
|
+
// Step 2: Wait for price element to load
|
|
506
|
+
await page.waitForSelector('.product-price', { timeout: 10000 });
|
|
507
|
+
|
|
508
|
+
// Step 3: Extract product details
|
|
509
|
+
const productData = await page.evaluate(() => {
|
|
510
|
+
return {
|
|
511
|
+
name: document.querySelector('.product-title')?.textContent.trim(),
|
|
512
|
+
price: document.querySelector('.product-price')?.textContent.trim(),
|
|
513
|
+
availability: document.querySelector('.stock-status')?.textContent.trim(),
|
|
514
|
+
rating: document.querySelector('.rating')?.textContent.trim(),
|
|
515
|
+
reviews: document.querySelector('.review-count')?.textContent.trim()
|
|
516
|
+
};
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
// Step 4: Take screenshot for record
|
|
520
|
+
await page.screenshot({ path: `price-${Date.now()}.png` });
|
|
521
|
+
|
|
522
|
+
// Step 5: Store data and alert if price changed
|
|
523
|
+
await fs.writeFile('price-history.json', JSON.stringify({
|
|
524
|
+
timestamp: new Date(),
|
|
525
|
+
...productData
|
|
526
|
+
}));
|
|
527
|
+
await browser.close();
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### 2. Automated Testing: Multi-Page Flow
|
|
531
|
+
|
|
532
|
+
```javascript
|
|
533
|
+
// Step 1: Setup browser and login
|
|
534
|
+
const browser = await puppeteer.launch({ headless: false });
|
|
535
|
+
const page = await browser.newPage();
|
|
536
|
+
await page.goto('https://app.example.com/login');
|
|
537
|
+
await page.type('#email', 'test@example.com');
|
|
538
|
+
await page.type('#password', 'TestPass123');
|
|
539
|
+
await page.click('button[type="submit"]');
|
|
540
|
+
await page.waitForNavigation();
|
|
541
|
+
|
|
542
|
+
// Step 2: Navigate through application
|
|
543
|
+
await page.click('a[href="/dashboard"]');
|
|
544
|
+
await page.waitForSelector('.dashboard-content');
|
|
545
|
+
|
|
546
|
+
// Step 3: Verify dashboard elements
|
|
547
|
+
const dashboardStats = await page.$$eval('.stat-card', cards => {
|
|
548
|
+
return cards.map(card => ({
|
|
549
|
+
label: card.querySelector('.stat-label')?.textContent,
|
|
550
|
+
value: card.querySelector('.stat-value')?.textContent
|
|
551
|
+
}));
|
|
552
|
+
});
|
|
553
|
+
console.log('Dashboard stats:', dashboardStats);
|
|
554
|
+
|
|
555
|
+
// Step 4: Test form submission
|
|
556
|
+
await page.click('button.new-item');
|
|
557
|
+
await page.waitForSelector('form.item-form');
|
|
558
|
+
await page.type('#item-name', 'Test Item');
|
|
559
|
+
await page.type('#item-description', 'This is a test');
|
|
560
|
+
await page.click('button.submit-item');
|
|
561
|
+
|
|
562
|
+
// Step 5: Verify success and cleanup
|
|
563
|
+
await page.waitForSelector('.success-notification');
|
|
564
|
+
const successMessage = await page.$eval('.success-notification', el => el.textContent);
|
|
565
|
+
console.log('Success:', successMessage);
|
|
566
|
+
await browser.close();
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
### 3. Data Extraction: Table Scraping
|
|
570
|
+
|
|
571
|
+
```javascript
|
|
572
|
+
// Step 1: Navigate to page with data table
|
|
573
|
+
const browser = await puppeteer.launch();
|
|
574
|
+
const page = await browser.newPage();
|
|
575
|
+
await page.goto('https://example.com/data-table');
|
|
576
|
+
|
|
577
|
+
// Step 2: Wait for table to load
|
|
578
|
+
await page.waitForSelector('table.data-table');
|
|
579
|
+
|
|
580
|
+
// Step 3: Extract all table data
|
|
581
|
+
const tableData = await page.evaluate(() => {
|
|
582
|
+
const rows = Array.from(document.querySelectorAll('table.data-table tbody tr'));
|
|
583
|
+
return rows.map(row => {
|
|
584
|
+
const cells = Array.from(row.querySelectorAll('td'));
|
|
585
|
+
return {
|
|
586
|
+
id: cells[0]?.textContent.trim(),
|
|
587
|
+
name: cells[1]?.textContent.trim(),
|
|
588
|
+
value: cells[2]?.textContent.trim(),
|
|
589
|
+
status: cells[3]?.textContent.trim(),
|
|
590
|
+
date: cells[4]?.textContent.trim()
|
|
591
|
+
};
|
|
592
|
+
});
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
// Step 4: Handle pagination
|
|
596
|
+
let allData = [...tableData];
|
|
597
|
+
let hasNextPage = await page.$('button.next-page:not([disabled])');
|
|
598
|
+
while (hasNextPage) {
|
|
599
|
+
await page.click('button.next-page');
|
|
600
|
+
await page.waitForTimeout(1000);
|
|
601
|
+
const pageData = await page.evaluate(/* same as step 3 */);
|
|
602
|
+
allData = [...allData, ...pageData];
|
|
603
|
+
hasNextPage = await page.$('button.next-page:not([disabled])');
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
// Step 5: Export data
|
|
607
|
+
await fs.writeFile('table-data.json', JSON.stringify(allData, null, 2));
|
|
608
|
+
await browser.close();
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### 4. Report Generation: Automated PDFs
|
|
612
|
+
|
|
613
|
+
```javascript
|
|
614
|
+
// Step 1: Navigate to report page
|
|
615
|
+
const browser = await puppeteer.launch();
|
|
616
|
+
const page = await browser.newPage();
|
|
617
|
+
await page.goto('https://analytics.example.com/report?id=123');
|
|
618
|
+
|
|
619
|
+
// Step 2: Wait for all charts to render
|
|
620
|
+
await page.waitForSelector('.chart-container canvas');
|
|
621
|
+
await page.waitForTimeout(2000); // Extra wait for animations
|
|
622
|
+
|
|
623
|
+
// Step 3: Hide unwanted elements
|
|
624
|
+
await page.addStyleTag({
|
|
625
|
+
content: '.no-print { display: none !important; }'
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// Step 4: Generate PDF with custom styling
|
|
629
|
+
await page.pdf({
|
|
630
|
+
path: 'report.pdf',
|
|
631
|
+
format: 'A4',
|
|
632
|
+
printBackground: true,
|
|
633
|
+
margin: { top: '20mm', bottom: '20mm', left: '15mm', right: '15mm' },
|
|
634
|
+
displayHeaderFooter: true,
|
|
635
|
+
headerTemplate: `
|
|
636
|
+
<div style="font-size:10px;width:100%;text-align:center;">
|
|
637
|
+
<span>Monthly Analytics Report</span>
|
|
638
|
+
</div>
|
|
639
|
+
`,
|
|
640
|
+
footerTemplate: `
|
|
641
|
+
<div style="font-size:10px;width:100%;text-align:center;">
|
|
642
|
+
<span class="pageNumber"></span> / <span class="totalPages"></span>
|
|
643
|
+
</div>
|
|
644
|
+
`
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
// Step 5: Cleanup
|
|
648
|
+
await browser.close();
|
|
649
|
+
console.log('PDF generated: report.pdf');
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
### 5. API Testing via Browser
|
|
653
|
+
|
|
654
|
+
```javascript
|
|
655
|
+
// Step 1: Setup request interception
|
|
656
|
+
const browser = await puppeteer.launch();
|
|
657
|
+
const page = await browser.newPage();
|
|
658
|
+
await page.setRequestInterception(true);
|
|
659
|
+
|
|
660
|
+
const apiCalls = [];
|
|
661
|
+
page.on('request', request => {
|
|
662
|
+
if (request.url().includes('/api/')) {
|
|
663
|
+
apiCalls.push({
|
|
664
|
+
url: request.url(),
|
|
665
|
+
method: request.method(),
|
|
666
|
+
headers: request.headers(),
|
|
667
|
+
postData: request.postData()
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
request.continue();
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
const apiResponses = [];
|
|
674
|
+
page.on('response', async response => {
|
|
675
|
+
if (response.url().includes('/api/')) {
|
|
676
|
+
apiResponses.push({
|
|
677
|
+
url: response.url(),
|
|
678
|
+
status: response.status(),
|
|
679
|
+
headers: response.headers(),
|
|
680
|
+
body: await response.text().catch(() => null)
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
|
|
685
|
+
// Step 2: Trigger API calls through UI
|
|
686
|
+
await page.goto('https://app.example.com');
|
|
687
|
+
await page.click('button.load-data');
|
|
688
|
+
await page.waitForTimeout(3000);
|
|
689
|
+
|
|
690
|
+
// Step 3: Verify API responses
|
|
691
|
+
const errors = apiResponses.filter(r => r.status >= 400);
|
|
692
|
+
console.log(`Total API calls: ${apiCalls.length}`);
|
|
693
|
+
console.log(`Failed requests: ${errors.length}`);
|
|
694
|
+
|
|
695
|
+
// Step 4: Analyze response times
|
|
696
|
+
const timings = apiResponses.map(r => ({
|
|
697
|
+
url: r.url,
|
|
698
|
+
time: r.timing?.responseEnd - r.timing?.requestStart
|
|
699
|
+
}));
|
|
700
|
+
|
|
701
|
+
// Step 5: Cleanup and report
|
|
702
|
+
await fs.writeFile('api-report.json', JSON.stringify({
|
|
703
|
+
calls: apiCalls,
|
|
704
|
+
responses: apiResponses,
|
|
705
|
+
errors
|
|
706
|
+
}, null, 2));
|
|
707
|
+
await browser.close();
|
|
708
|
+
```
|