@mozilla/firefox-devtools-mcp-moz 0.9.3
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 +219 -0
- package/dist.moz/index.js +33246 -0
- package/dist.moz/snapshot.injected.global.js +1 -0
- package/package.json +79 -0
- package/plugins/claude/firefox-devtools/.claude-plugin/plugin.json +11 -0
- package/plugins/claude/firefox-devtools/.mcp.json +8 -0
- package/plugins/claude/firefox-devtools/README.md +82 -0
- package/plugins/claude/firefox-devtools/agents/e2e-tester.md +36 -0
- package/plugins/claude/firefox-devtools/agents/web-scraper.md +35 -0
- package/plugins/claude/firefox-devtools/commands/debug.md +32 -0
- package/plugins/claude/firefox-devtools/commands/navigate.md +31 -0
- package/plugins/claude/firefox-devtools/commands/screenshot.md +30 -0
- package/plugins/claude/firefox-devtools/skills/browser-automation/SKILL.md +65 -0
- package/scripts/_helpers/page-loader.js +121 -0
- package/scripts/demo-server.js +325 -0
- package/scripts/generate-moz-package.mjs +35 -0
- package/scripts/publish-moz-package.mjs +52 -0
- package/scripts/run-integration-tests-windows.mjs +188 -0
- package/scripts/setup-mcp-config.js +260 -0
- package/scripts/test-bidi-devtools.js +380 -0
- package/scripts/test-console.js +148 -0
- package/scripts/test-dialog.js +102 -0
- package/scripts/test-firefox-temp.js +21 -0
- package/scripts/test-input-tools.js +233 -0
- package/scripts/test-lifecycle-hooks.js +125 -0
- package/scripts/test-mozlog.js +112 -0
- package/scripts/test-navigation.js +57 -0
- package/scripts/test-privileged-context.js +99 -0
- package/scripts/test-screenshot.js +190 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { FirefoxDevTools } from '../dist/index.js';
|
|
4
|
+
import { readFileSync, existsSync } from 'fs';
|
|
5
|
+
|
|
6
|
+
async function test() {
|
|
7
|
+
console.log('=== Test: MOZ_LOG and Script Injection (headless) ===\n');
|
|
8
|
+
|
|
9
|
+
const logFile = '/tmp/firefox-mozlog-test.log';
|
|
10
|
+
|
|
11
|
+
const firefox = new FirefoxDevTools({
|
|
12
|
+
headless: true,
|
|
13
|
+
firefoxPath: process.env.FIREFOX_PATH,
|
|
14
|
+
env: {
|
|
15
|
+
MOZ_LOG: 'timestamp,sync,nsHttp:5',
|
|
16
|
+
},
|
|
17
|
+
logFile,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
await firefox.connect();
|
|
21
|
+
console.log('✓ Firefox started in headless mode with MOZ_LOG');
|
|
22
|
+
console.log(` Log file: ${logFile}`);
|
|
23
|
+
|
|
24
|
+
// Test content script evaluation
|
|
25
|
+
console.log('\n--- Testing content script evaluation ---');
|
|
26
|
+
await firefox.navigate('https://example.com');
|
|
27
|
+
console.log('✓ Navigated to example.com');
|
|
28
|
+
|
|
29
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const title = await firefox.evaluate('return document.title');
|
|
33
|
+
console.log(`✓ Content script: document.title = "${title}"`);
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.log(`✗ Content script evaluation failed: ${err.message}`);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const url = await firefox.evaluate('return window.location.href');
|
|
40
|
+
console.log(`✓ Content script: window.location.href = "${url}"`);
|
|
41
|
+
} catch (err) {
|
|
42
|
+
console.log(`✗ Failed to access window.location: ${err.message}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const headings = await firefox.evaluate('return document.querySelectorAll("h1").length');
|
|
47
|
+
console.log(`✓ Content script: found ${headings} h1 elements`);
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.log(`✗ Failed to query DOM: ${err.message}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Navigate to another page
|
|
53
|
+
await firefox.navigate('https://mozilla.org');
|
|
54
|
+
console.log('✓ Navigated to mozilla.org');
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const title2 = await firefox.evaluate('return document.title');
|
|
59
|
+
console.log(`✓ Content script: document.title = "${title2}"`);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.log(`✗ Content script evaluation failed: ${err.message}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Check log file
|
|
65
|
+
console.log('\n--- Checking MOZ_LOG output ---');
|
|
66
|
+
|
|
67
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
68
|
+
|
|
69
|
+
await firefox.close();
|
|
70
|
+
console.log('✓ Firefox closed');
|
|
71
|
+
|
|
72
|
+
// Give a moment for log file to be flushed
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
74
|
+
|
|
75
|
+
if (existsSync(logFile)) {
|
|
76
|
+
try {
|
|
77
|
+
const logContent = readFileSync(logFile, 'utf8');
|
|
78
|
+
const lines = logContent.split('\n').filter((l) => l.trim());
|
|
79
|
+
console.log(`✓ Log file exists with ${lines.length} lines`);
|
|
80
|
+
|
|
81
|
+
// Check for HTTP logging
|
|
82
|
+
const httpLines = lines.filter((l) => l.includes('nsHttp'));
|
|
83
|
+
console.log(` Found ${httpLines.length} nsHttp log lines`);
|
|
84
|
+
|
|
85
|
+
if (httpLines.length > 0) {
|
|
86
|
+
console.log(' Sample HTTP log lines:');
|
|
87
|
+
httpLines.slice(0, 3).forEach((line) => {
|
|
88
|
+
console.log(` ${line.substring(0, 100)}`);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check for timestamps
|
|
93
|
+
const timestampLines = lines.filter((l) => /^\d{4}-\d{2}-\d{2}/.test(l));
|
|
94
|
+
console.log(` Found ${timestampLines.length} timestamped lines`);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
console.log(`✗ Could not read log file: ${err.message}`);
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
console.log(`✗ Log file does not exist: ${logFile}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log('\n✓ All feature tests completed!');
|
|
103
|
+
console.log('\nNote: Privileged context evaluation requires:');
|
|
104
|
+
console.log(' - MOZ_REMOTE_ALLOW_SYSTEM_ACCESS=1 env var');
|
|
105
|
+
console.log(' - Using restart_firefox tool or npm run inspector');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
test().catch((err) => {
|
|
109
|
+
console.error('\nTest failed:', err);
|
|
110
|
+
console.error(err.stack);
|
|
111
|
+
process.exit(1);
|
|
112
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { FirefoxDevTools } from '../dist/index.js';
|
|
4
|
+
|
|
5
|
+
async function test() {
|
|
6
|
+
console.log('=== Test: Start Firefox and navigate ===\n');
|
|
7
|
+
|
|
8
|
+
const firefox = new FirefoxDevTools({
|
|
9
|
+
headless: false,
|
|
10
|
+
firefoxPath: process.env.FIREFOX_PATH,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
await firefox.connect();
|
|
14
|
+
console.log('✓ Firefox started');
|
|
15
|
+
|
|
16
|
+
await firefox.navigate('https://example.com');
|
|
17
|
+
console.log('✓ Navigated to example.com');
|
|
18
|
+
|
|
19
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
20
|
+
|
|
21
|
+
await firefox.navigate('https://mozilla.org');
|
|
22
|
+
console.log('✓ Navigated to mozilla.org');
|
|
23
|
+
|
|
24
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
25
|
+
|
|
26
|
+
await firefox.refreshTabs();
|
|
27
|
+
const tabs = firefox.getTabs();
|
|
28
|
+
console.log(`✓ Listed tabs: ${tabs.length} tab(s)`);
|
|
29
|
+
if (tabs.length > 0) {
|
|
30
|
+
console.log(` Current URL: ${tabs[0].url}`);
|
|
31
|
+
console.log(` Current title: ${tabs[0].title || 'N/A'}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
35
|
+
|
|
36
|
+
await firefox.navigate('https://www.w3.org');
|
|
37
|
+
console.log('✓ Navigated to w3.org');
|
|
38
|
+
|
|
39
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
40
|
+
|
|
41
|
+
await firefox.refreshTabs();
|
|
42
|
+
const tabsAfter = firefox.getTabs();
|
|
43
|
+
console.log(`✓ Listed tabs again: ${tabsAfter.length} tab(s)`);
|
|
44
|
+
if (tabsAfter.length > 0) {
|
|
45
|
+
console.log(` Current URL: ${tabsAfter[0].url}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await firefox.close();
|
|
49
|
+
console.log('\n✓ Basic navigation tests passed!');
|
|
50
|
+
console.log('\nNote: To test restart_firefox with logs, use the MCP inspector:');
|
|
51
|
+
console.log(' npm run inspector');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
test().catch(err => {
|
|
55
|
+
console.error('\nTest failed:', err);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { FirefoxDevTools } from '../dist/index.js';
|
|
4
|
+
|
|
5
|
+
async function test() {
|
|
6
|
+
console.log('=== Test: Privileged Context Script Evaluation (headless) ===\n');
|
|
7
|
+
|
|
8
|
+
const firefox = new FirefoxDevTools({
|
|
9
|
+
headless: true,
|
|
10
|
+
firefoxPath: process.env.FIREFOX_PATH,
|
|
11
|
+
env: {
|
|
12
|
+
MOZ_REMOTE_ALLOW_SYSTEM_ACCESS: '1',
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
await firefox.connect();
|
|
17
|
+
console.log('✓ Firefox started with MOZ_REMOTE_ALLOW_SYSTEM_ACCESS=1');
|
|
18
|
+
|
|
19
|
+
// Test content script first
|
|
20
|
+
console.log('\n--- Testing content script (default context) ---');
|
|
21
|
+
await firefox.navigate('https://example.com');
|
|
22
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const title = await firefox.evaluate('return document.title');
|
|
26
|
+
console.log(`✓ Content context: document.title = "${title}"`);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
console.log(`✗ Content script failed: ${err.message}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Now test privileged context via BiDi with moz:scope
|
|
32
|
+
console.log('\n--- Testing privileged context listing ---');
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// Use BiDi to list privileged contexts with moz:scope
|
|
36
|
+
const result = await firefox.sendBiDiCommand('browsingContext.getTree', {
|
|
37
|
+
'moz:scope': 'chrome',
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const contexts = result.contexts || [];
|
|
41
|
+
console.log(`✓ Listed ${contexts.length} privileged context(s) via BiDi`);
|
|
42
|
+
|
|
43
|
+
if (contexts.length > 0) {
|
|
44
|
+
console.log(' Sample privileged contexts:');
|
|
45
|
+
contexts.slice(0, 3).forEach((ctx) => {
|
|
46
|
+
console.log(` ${ctx.context}: ${ctx.url || '(no url)'}`);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// Try to evaluate in privileged context
|
|
50
|
+
console.log('\n--- Testing privileged script execution ---');
|
|
51
|
+
|
|
52
|
+
const driver = firefox.getDriver();
|
|
53
|
+
const firstContext = contexts[0];
|
|
54
|
+
|
|
55
|
+
// Switch to chrome browsing context
|
|
56
|
+
await driver.switchTo().window(firstContext.context);
|
|
57
|
+
console.log(`✓ Switched to privileged context: ${firstContext.context}`);
|
|
58
|
+
|
|
59
|
+
// Set Marionette context to chrome
|
|
60
|
+
try {
|
|
61
|
+
await driver.setContext('chrome');
|
|
62
|
+
console.log('✓ Set Marionette context to "chrome"');
|
|
63
|
+
|
|
64
|
+
// Now try to evaluate chrome-privileged script
|
|
65
|
+
const appName = await driver.executeScript('return Services.appinfo.name;');
|
|
66
|
+
console.log(`✓ Privileged script: Services.appinfo.name = "${appName}"`);
|
|
67
|
+
|
|
68
|
+
const version = await driver.executeScript('return Services.appinfo.version;');
|
|
69
|
+
console.log(`✓ Privileged script: Services.appinfo.version = "${version}"`);
|
|
70
|
+
|
|
71
|
+
const buildID = await driver.executeScript('return Services.appinfo.appBuildID;');
|
|
72
|
+
console.log(`✓ Privileged script: Services.appinfo.appBuildID = "${buildID}"`);
|
|
73
|
+
|
|
74
|
+
console.log('\n✅ Privileged context evaluation WORKS!');
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.log(`✗ Failed to set privileged context: ${err.message}`);
|
|
77
|
+
console.log(' Your Firefox build may not support privileged context');
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
console.log(' No privileged contexts found (requires dev/nightly build)');
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (err.message && err.message.includes('UnsupportedOperationError')) {
|
|
84
|
+
console.log('✗ Privileged context not supported by this Firefox build');
|
|
85
|
+
console.log(' Requires Firefox Nightly or custom build');
|
|
86
|
+
} else {
|
|
87
|
+
console.log(`✗ Privileged context test failed: ${err.message}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await firefox.close();
|
|
92
|
+
console.log('\n✓ Test completed');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
test().catch((err) => {
|
|
96
|
+
console.error('\nTest failed:', err);
|
|
97
|
+
console.error(err.stack);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
});
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test script for screenshot functionality (Task 22)
|
|
5
|
+
* Tests: takeScreenshotPage, takeScreenshotByUid
|
|
6
|
+
* Saves screenshots to temp/ directory for visual inspection
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { FirefoxDevTools } from '../dist/index.js';
|
|
10
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { dirname } from 'node:path';
|
|
14
|
+
import {
|
|
15
|
+
loadHTML,
|
|
16
|
+
waitShort,
|
|
17
|
+
shouldRunOnlineTests,
|
|
18
|
+
skipOnlineTest,
|
|
19
|
+
} from './_helpers/page-loader.js';
|
|
20
|
+
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = dirname(__filename);
|
|
23
|
+
const TEMP_DIR = join(__dirname, '../temp');
|
|
24
|
+
|
|
25
|
+
async function saveScreenshot(base64Data, filename) {
|
|
26
|
+
// Ensure temp directory exists
|
|
27
|
+
await mkdir(TEMP_DIR, { recursive: true });
|
|
28
|
+
|
|
29
|
+
// Convert base64 to buffer and save
|
|
30
|
+
const buffer = Buffer.from(base64Data, 'base64');
|
|
31
|
+
const filepath = join(TEMP_DIR, filename);
|
|
32
|
+
await writeFile(filepath, buffer);
|
|
33
|
+
|
|
34
|
+
console.log(` 💾 Saved: ${filepath} (${(buffer.length / 1024).toFixed(2)} KB)`);
|
|
35
|
+
return filepath;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function main() {
|
|
39
|
+
console.log('📷 Testing Screenshot Functionality...\n');
|
|
40
|
+
|
|
41
|
+
const firefox = new FirefoxDevTools({
|
|
42
|
+
firefoxPath: process.env.FIREFOX_PATH,
|
|
43
|
+
headless: false,
|
|
44
|
+
startUrl: 'about:blank',
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
console.log('📡 Connecting to Firefox...');
|
|
49
|
+
await firefox.connect();
|
|
50
|
+
console.log('✅ Connected!\n');
|
|
51
|
+
|
|
52
|
+
// Online tests (optional)
|
|
53
|
+
if (shouldRunOnlineTests()) {
|
|
54
|
+
// Test 1: Screenshot of example.com
|
|
55
|
+
console.log('🌐 Test 1: Full page screenshot (example.com)');
|
|
56
|
+
await firefox.navigate('https://example.com');
|
|
57
|
+
await waitShort(2000);
|
|
58
|
+
|
|
59
|
+
const examplePageScreenshot = await firefox.takeScreenshotPage();
|
|
60
|
+
await saveScreenshot(examplePageScreenshot, 'screenshot-example-page.png');
|
|
61
|
+
console.log(` ✅ Screenshot captured (${examplePageScreenshot.length} chars base64)\n`);
|
|
62
|
+
|
|
63
|
+
// Test 2: Screenshot of specific element (heading)
|
|
64
|
+
console.log('🎯 Test 2: Element screenshot (h1 heading)');
|
|
65
|
+
const snapshot1 = await firefox.takeSnapshot();
|
|
66
|
+
const h1Node = snapshot1.json.root.children?.find((n) => n.tag === 'h1');
|
|
67
|
+
|
|
68
|
+
if (h1Node && h1Node.uid) {
|
|
69
|
+
console.log(` Found: <h1> with UID ${h1Node.uid}`);
|
|
70
|
+
const h1Screenshot = await firefox.takeScreenshotByUid(h1Node.uid);
|
|
71
|
+
await saveScreenshot(h1Screenshot, 'screenshot-example-h1.png');
|
|
72
|
+
console.log(` ✅ Element screenshot captured (${h1Screenshot.length} chars base64)\n`);
|
|
73
|
+
} else {
|
|
74
|
+
console.log(' ⚠️ No h1 element found\n');
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
skipOnlineTest('Online screenshot tests (example.com)');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Test 3: Custom HTML page with styled elements (OFFLINE)
|
|
81
|
+
console.log('🎨 Test 3: Custom styled page (offline)');
|
|
82
|
+
|
|
83
|
+
await loadHTML(
|
|
84
|
+
firefox,
|
|
85
|
+
`
|
|
86
|
+
<head><title>Screenshot Test</title><style>
|
|
87
|
+
body { font-family: Arial; padding: 40px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); margin: 0; min-height: 100vh; }
|
|
88
|
+
.card { background: white; border-radius: 12px; padding: 30px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); max-width: 600px; margin: 0 auto; }
|
|
89
|
+
h1 { color: #667eea; margin: 0 0 20px 0; font-size: 36px; }
|
|
90
|
+
.button { background: #667eea; color: white; padding: 15px 30px; border: none; border-radius: 8px; font-size: 18px; cursor: pointer; display: inline-block; margin: 10px 5px; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); }
|
|
91
|
+
.button:hover { transform: translateY(-2px); box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6); }
|
|
92
|
+
p { color: #555; line-height: 1.6; font-size: 16px; }
|
|
93
|
+
</style></head><body>
|
|
94
|
+
<div class="card">
|
|
95
|
+
<h1 id="title">🎯 Screenshot Test Page</h1>
|
|
96
|
+
<p id="description">This is a beautifully styled test page for screenshot functionality. The gradient background and card design showcase visual capture capabilities.</p>
|
|
97
|
+
<button class="button" id="btn1">Primary Action</button>
|
|
98
|
+
<button class="button" id="btn2">Secondary Action</button>
|
|
99
|
+
</div>
|
|
100
|
+
</body>
|
|
101
|
+
`
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
await waitShort(500);
|
|
105
|
+
|
|
106
|
+
const customPageScreenshot = await firefox.takeScreenshotPage();
|
|
107
|
+
await saveScreenshot(customPageScreenshot, 'screenshot-custom-page.png');
|
|
108
|
+
console.log(` ✅ Full page screenshot captured\n`);
|
|
109
|
+
|
|
110
|
+
// Test 4: Screenshot of styled elements using UID
|
|
111
|
+
console.log('🎨 Test 4: Individual styled elements (via UID)');
|
|
112
|
+
|
|
113
|
+
const snapshot = await firefox.takeSnapshot();
|
|
114
|
+
|
|
115
|
+
// Find h1 element
|
|
116
|
+
const findElement = (node, tag) => {
|
|
117
|
+
if (node.tag === tag) return node;
|
|
118
|
+
if (node.children) {
|
|
119
|
+
for (const child of node.children) {
|
|
120
|
+
const found = findElement(child, tag);
|
|
121
|
+
if (found) return found;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return null;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const h1 = findElement(snapshot.json.root, 'h1');
|
|
128
|
+
if (h1 && h1.uid) {
|
|
129
|
+
const h1Screenshot = await firefox.takeScreenshotByUid(h1.uid);
|
|
130
|
+
await saveScreenshot(h1Screenshot, 'screenshot-custom-h1.png');
|
|
131
|
+
console.log(` ✅ H1 screenshot captured via UID`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Find first button
|
|
135
|
+
const button = findElement(snapshot.json.root, 'button');
|
|
136
|
+
if (button && button.uid) {
|
|
137
|
+
const buttonScreenshot = await firefox.takeScreenshotByUid(button.uid);
|
|
138
|
+
await saveScreenshot(buttonScreenshot, 'screenshot-custom-button.png');
|
|
139
|
+
console.log(` ✅ Button screenshot captured via UID\n`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Test 5: Screenshot using direct CSS selectors (fallback method)
|
|
143
|
+
console.log('🎨 Test 5: Individual styled elements (via CSS selectors)');
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
// Get element via evaluate and take screenshot using WebDriver directly
|
|
147
|
+
const driver = firefox.getDriver();
|
|
148
|
+
|
|
149
|
+
// Screenshot title
|
|
150
|
+
const titleEl = await driver.findElement({ css: '#title' });
|
|
151
|
+
await driver.executeScript('arguments[0].scrollIntoView({block: "center"});', titleEl);
|
|
152
|
+
await waitShort(200);
|
|
153
|
+
const titleScreenshot = await titleEl.takeScreenshot();
|
|
154
|
+
await saveScreenshot(titleScreenshot, 'screenshot-custom-title.png');
|
|
155
|
+
console.log(` ✅ Title screenshot captured`);
|
|
156
|
+
|
|
157
|
+
// Screenshot second button (avoid conflict with UID test)
|
|
158
|
+
const buttonEl = await driver.findElement({ css: '#btn2' });
|
|
159
|
+
await driver.executeScript('arguments[0].scrollIntoView({block: "center"});', buttonEl);
|
|
160
|
+
await waitShort(200);
|
|
161
|
+
const buttonScreenshot = await buttonEl.takeScreenshot();
|
|
162
|
+
await saveScreenshot(buttonScreenshot, 'screenshot-custom-button2.png');
|
|
163
|
+
console.log(` ✅ Button screenshot captured`);
|
|
164
|
+
|
|
165
|
+
// Screenshot card container
|
|
166
|
+
const cardEl = await driver.findElement({ css: '.card' });
|
|
167
|
+
await driver.executeScript('arguments[0].scrollIntoView({block: "center"});', cardEl);
|
|
168
|
+
await waitShort(200);
|
|
169
|
+
const cardScreenshot = await cardEl.takeScreenshot();
|
|
170
|
+
await saveScreenshot(cardScreenshot, 'screenshot-custom-card.png');
|
|
171
|
+
console.log(` ✅ Card screenshot captured\n`);
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.log(` ⚠️ Element screenshot failed: ${error.message}\n`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.log('✅ All screenshot tests completed! 🎉');
|
|
177
|
+
console.log(`\n📁 Screenshots saved to: ${TEMP_DIR}\n`);
|
|
178
|
+
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error('❌ Test failed:', error.message);
|
|
181
|
+
if (error.stack) console.error(error.stack);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
} finally {
|
|
184
|
+
console.log('🧹 Closing...');
|
|
185
|
+
await firefox.close();
|
|
186
|
+
console.log('✅ Done');
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
main().catch(console.error);
|