firefox-devtools-mcp 0.2.0

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.
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Standalone test for console message handling
5
+ * Tests console.log, console.warn, console.error capturing
6
+ */
7
+
8
+ import { FirefoxDevTools } from '../dist/index.js';
9
+
10
+ async function main() {
11
+ console.log('๐Ÿ“ Testing Console Message Handling...\n');
12
+
13
+ const firefox = new FirefoxDevTools({
14
+ firefoxPath: undefined,
15
+ headless: false,
16
+ viewport: { width: 1024, height: 768 },
17
+ });
18
+
19
+ try {
20
+ // Connect
21
+ console.log('๐Ÿ“ก Connecting to Firefox...');
22
+ await firefox.connect();
23
+ console.log('โœ… Connected!\n');
24
+
25
+ // Navigate to blank page
26
+ console.log('๐ŸŒ Navigating to about:blank...');
27
+ await firefox.navigate('about:blank');
28
+ console.log('โœ… Navigation complete\n');
29
+
30
+ // Clear existing console messages
31
+ console.log('๐Ÿงน Clearing existing console messages...');
32
+ firefox.clearConsoleMessages();
33
+ const afterClear = await firefox.getConsoleMessages();
34
+ console.log(`โœ… Console cleared (${afterClear.length} messages)\n`);
35
+
36
+ // Test 1: Generate different log levels
37
+ console.log('1๏ธโƒฃ Generating console messages of different levels...');
38
+ await firefox.evaluate(`
39
+ console.log('This is a log message');
40
+ console.info('This is an info message');
41
+ console.warn('This is a warning message');
42
+ console.error('This is an error message');
43
+ console.debug('This is a debug message');
44
+ `);
45
+
46
+ // Wait a bit for messages to be captured
47
+ await new Promise((r) => setTimeout(r, 500));
48
+
49
+ const allMessages = await firefox.getConsoleMessages();
50
+ console.log(` โœ… Captured ${allMessages.length} console messages\n`);
51
+
52
+ // Display all messages
53
+ console.log('๐Ÿ“‹ All console messages:');
54
+ for (const msg of allMessages) {
55
+ const emoji = {
56
+ log: '๐Ÿ“',
57
+ info: 'โ„น๏ธ',
58
+ warn: 'โš ๏ธ',
59
+ error: 'โŒ',
60
+ debug: '๐Ÿ”',
61
+ }[msg.level.toLowerCase()] || '๐Ÿ“';
62
+ console.log(` ${emoji} [${msg.level}] ${msg.text}`);
63
+ }
64
+ console.log();
65
+
66
+ // Test 2: Filter by level
67
+ console.log('2๏ธโƒฃ Testing level filtering...');
68
+ const errors = allMessages.filter((msg) => msg.level.toLowerCase() === 'error');
69
+ const warnings = allMessages.filter((msg) => msg.level.toLowerCase() === 'warn');
70
+ console.log(` โœ… Found ${errors.length} error(s)`);
71
+ console.log(` โœ… Found ${warnings.length} warning(s)\n`);
72
+
73
+ // Test 3: Generate many messages and test limit
74
+ console.log('3๏ธโƒฃ Testing message limit (generating 100 messages)...');
75
+ firefox.clearConsoleMessages();
76
+ await firefox.evaluate(`
77
+ for (let i = 0; i < 100; i++) {
78
+ console.log('Message ' + i);
79
+ }
80
+ `);
81
+
82
+ await new Promise((r) => setTimeout(r, 1000));
83
+ const manyMessages = await firefox.getConsoleMessages();
84
+ console.log(` โœ… Captured ${manyMessages.length} messages\n`);
85
+
86
+ // Test 4: Clear messages
87
+ console.log('4๏ธโƒฃ Testing clear console messages...');
88
+ firefox.clearConsoleMessages();
89
+ const afterSecondClear = await firefox.getConsoleMessages();
90
+ console.log(` โœ… Console cleared (${afterSecondClear.length} messages remaining)\n`);
91
+
92
+ // Test 5: Test timestamps
93
+ console.log('5๏ธโƒฃ Testing message timestamps...');
94
+ firefox.clearConsoleMessages();
95
+ const startTime = Date.now();
96
+
97
+ await firefox.evaluate(`console.log('Message at T=0')`);
98
+ await new Promise((r) => setTimeout(r, 500));
99
+ await firefox.evaluate(`console.log('Message at T=500ms')`);
100
+ await new Promise((r) => setTimeout(r, 500));
101
+ await firefox.evaluate(`console.log('Message at T=1000ms')`);
102
+
103
+ await new Promise((r) => setTimeout(r, 500));
104
+ const timedMessages = await firefox.getConsoleMessages();
105
+ console.log(` โœ… Captured ${timedMessages.length} timed messages`);
106
+
107
+ for (const msg of timedMessages) {
108
+ const elapsed = msg.timestamp ? msg.timestamp - startTime : 'N/A';
109
+ console.log(` ๐Ÿ“ "${msg.text}" at +${elapsed}ms`);
110
+ }
111
+ console.log();
112
+
113
+ // Test 6: Test with objects and complex data
114
+ console.log('6๏ธโƒฃ Testing console with objects and arrays...');
115
+ firefox.clearConsoleMessages();
116
+ await firefox.evaluate(`
117
+ console.log('Simple string');
118
+ console.log('Number:', 42);
119
+ console.log('Object:', { name: 'Test', value: 123 });
120
+ console.log('Array:', [1, 2, 3, 4, 5]);
121
+ `);
122
+
123
+ await new Promise((r) => setTimeout(r, 500));
124
+ const complexMessages = await firefox.getConsoleMessages();
125
+ console.log(` โœ… Captured ${complexMessages.length} messages with complex data`);
126
+ for (const msg of complexMessages) {
127
+ console.log(` ๐Ÿ“ ${msg.text}`);
128
+ }
129
+ console.log();
130
+
131
+ console.log('โœ… All console tests passed! ๐ŸŽ‰\n');
132
+ } catch (error) {
133
+ console.error('โŒ Test failed:', error.message);
134
+ if (error.stack) {
135
+ console.error(error.stack);
136
+ }
137
+ process.exit(1);
138
+ } finally {
139
+ console.log('๐Ÿงน Closing Firefox...');
140
+ await firefox.close();
141
+ console.log('โœ… Done!');
142
+ }
143
+ }
144
+
145
+ main().catch((error) => {
146
+ console.error('๐Ÿ’ฅ Fatal error:', error);
147
+ process.exit(1);
148
+ });
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Standalone test for dialog handling (Task 23)
5
+ */
6
+
7
+ import { FirefoxDevTools } from '../dist/index.js';
8
+
9
+ async function main() {
10
+ console.log('๐Ÿ’ฌ Testing Dialog Handling (Task 23)...\n');
11
+
12
+ const firefox = new FirefoxDevTools({
13
+ firefoxPath: undefined,
14
+ headless: false,
15
+ viewport: { width: 1024, height: 768 },
16
+ });
17
+
18
+ try {
19
+ // Connect
20
+ console.log('๐Ÿ“ก Connecting to Firefox...');
21
+ await firefox.connect();
22
+ console.log('โœ… Connected!\n');
23
+
24
+ // Test 1: Alert dialog
25
+ console.log('1๏ธโƒฃ Testing alert() dialog...');
26
+ await firefox.navigate('about:blank');
27
+ await firefox.evaluate('setTimeout(() => alert("This is a test alert!"), 100)');
28
+ await new Promise((r) => setTimeout(r, 300));
29
+ await firefox.acceptDialog();
30
+ console.log(' โœ… Alert accepted\n');
31
+
32
+ // Test 2: Confirm dialog - accept
33
+ console.log('2๏ธโƒฃ Testing confirm() dialog - accept...');
34
+ await firefox.evaluate(
35
+ 'setTimeout(() => { window.confirmResult = confirm("Click OK to accept"); }, 100)'
36
+ );
37
+ await new Promise((r) => setTimeout(r, 300));
38
+ await firefox.acceptDialog();
39
+ const confirmAccepted = await firefox.evaluate('return window.confirmResult');
40
+ console.log(` Result: ${confirmAccepted}`);
41
+ console.log(` ${confirmAccepted ? 'โœ…' : 'โŒ'} Confirm was accepted\n`);
42
+
43
+ // Test 3: Confirm dialog - dismiss
44
+ console.log('3๏ธโƒฃ Testing confirm() dialog - dismiss...');
45
+ await firefox.evaluate(
46
+ 'setTimeout(() => { window.confirmResult2 = confirm("Click Cancel to dismiss"); }, 100)'
47
+ );
48
+ await new Promise((r) => setTimeout(r, 300));
49
+ await firefox.dismissDialog();
50
+ const confirmDismissed = await firefox.evaluate('return window.confirmResult2');
51
+ console.log(` Result: ${confirmDismissed}`);
52
+ console.log(` ${!confirmDismissed ? 'โœ…' : 'โŒ'} Confirm was dismissed\n`);
53
+
54
+ // Test 4: Prompt dialog with text
55
+ console.log('4๏ธโƒฃ Testing prompt() dialog with custom text...');
56
+ await firefox.evaluate(
57
+ 'setTimeout(() => { window.promptResult = prompt("Enter your favorite color:"); }, 100)'
58
+ );
59
+ await new Promise((r) => setTimeout(r, 300));
60
+ await firefox.acceptDialog('Blue');
61
+ const promptResult = await firefox.evaluate('return window.promptResult');
62
+ console.log(` Result: ${promptResult}`);
63
+ console.log(` ${promptResult === 'Blue' ? 'โœ…' : 'โŒ'} Prompt returned: "${promptResult}"\n`);
64
+
65
+ // Test 5: Prompt dialog dismissed
66
+ console.log('5๏ธโƒฃ Testing prompt() dialog - dismiss...');
67
+ await firefox.evaluate(
68
+ 'setTimeout(() => { window.promptResult2 = prompt("This will be dismissed"); }, 100)'
69
+ );
70
+ await new Promise((r) => setTimeout(r, 300));
71
+ await firefox.dismissDialog();
72
+ const promptDismissed = await firefox.evaluate('return window.promptResult2');
73
+ console.log(` Result: ${promptDismissed}`);
74
+ console.log(` ${promptDismissed === null ? 'โœ…' : 'โŒ'} Prompt was dismissed (null)\n`);
75
+
76
+ // Test 6: Error handling - no dialog present
77
+ console.log('6๏ธโƒฃ Testing error handling (no dialog present)...');
78
+ try {
79
+ await firefox.acceptDialog();
80
+ console.log(' โŒ Should have thrown error');
81
+ } catch (e) {
82
+ console.log(` โœ… Error caught: ${e.message}\n`);
83
+ }
84
+
85
+ console.log('โœ… All dialog tests passed! ๐ŸŽ‰\n');
86
+ } catch (error) {
87
+ console.error('โŒ Test failed:', error.message);
88
+ if (error.stack) {
89
+ console.error(error.stack);
90
+ }
91
+ process.exit(1);
92
+ } finally {
93
+ console.log('๐Ÿงน Closing Firefox...');
94
+ await firefox.close();
95
+ console.log('โœ… Done!');
96
+ }
97
+ }
98
+
99
+ main().catch((error) => {
100
+ console.error('๐Ÿ’ฅ Fatal error:', error);
101
+ process.exit(1);
102
+ });
@@ -0,0 +1,233 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Test script for UID-based input tools (Task 21)
5
+ * Tests: clickByUid, fillByUid, hoverByUid, dragByUidToUid, fillFormByUid, uploadFileByUid
6
+ *
7
+ * Note: Uses innerHTML injection to avoid Firefox data: URL parsing issues
8
+ */
9
+
10
+ import { FirefoxDevTools } from '../dist/index.js';
11
+ import { writeFile, mkdtemp, rm } from 'node:fs/promises';
12
+ import { join } from 'node:path';
13
+ import { tmpdir } from 'node:os';
14
+ import { loadHTML, waitShort } from './_helpers/page-loader.js';
15
+
16
+ async function main() {
17
+ console.log('๐Ÿงช Testing UID-based input tools...\n');
18
+
19
+ const firefox = new FirefoxDevTools({
20
+ firefoxPath: undefined,
21
+ headless: false,
22
+ startUrl: 'about:blank',
23
+ });
24
+
25
+ try {
26
+ console.log('๐Ÿ“ก Connecting to Firefox...');
27
+ await firefox.connect();
28
+ console.log('โœ… Connected!\n');
29
+
30
+ // Test 1: Click
31
+ console.log('๐Ÿ–ฑ๏ธ Test 1: Click By UID');
32
+ await loadHTML(firefox, `
33
+ <head><title>Test</title></head>
34
+ <body>
35
+ <button id="btn">Click Me</button>
36
+ <script>
37
+ document.getElementById('btn').addEventListener('click', () => {
38
+ document.body.setAttribute('data-result', 'clicked');
39
+ });
40
+ </script>
41
+ </body>
42
+ `);
43
+ let snapshot = await firefox.takeSnapshot();
44
+ const btnUid = snapshot.json.root.children.find(n => n.tag === 'button')?.uid;
45
+ if (btnUid) {
46
+ await firefox.clickByUid(btnUid);
47
+ await waitShort();
48
+ const result = await firefox.evaluate("return document.body.getAttribute('data-result')");
49
+ console.log(` ${result === 'clicked' ? 'โœ…' : 'โŒ'} Click: ${result}\n`);
50
+ } else {
51
+ console.log(' โŒ Button UID not found\n');
52
+ }
53
+
54
+ // Test 2: Fill
55
+ console.log('โœ๏ธ Test 2: Fill By UID');
56
+ await loadHTML(firefox, `
57
+ <head><title>Test</title></head>
58
+ <body>
59
+ <input id="inp" type="text">
60
+ <script>
61
+ document.getElementById('inp').addEventListener('input', (e) => {
62
+ document.body.setAttribute('data-value', e.target.value);
63
+ });
64
+ </script>
65
+ </body>
66
+ `);
67
+ snapshot = await firefox.takeSnapshot();
68
+ const inpUid = snapshot.json.root.children.find(n => n.tag === 'input')?.uid;
69
+ if (inpUid) {
70
+ await firefox.fillByUid(inpUid, 'Hello Test');
71
+ await waitShort();
72
+ const value = await firefox.evaluate("return document.body.getAttribute('data-value')");
73
+ console.log(` ${value === 'Hello Test' ? 'โœ…' : 'โŒ'} Fill: ${value}\n`);
74
+ } else {
75
+ console.log(' โŒ Input UID not found\n');
76
+ }
77
+
78
+ // Test 3: Hover
79
+ console.log('๐ŸŽฏ Test 3: Hover By UID');
80
+ await loadHTML(firefox, `
81
+ <head><title>Test</title></head>
82
+ <body>
83
+ <div id="hover">Hover Me</div>
84
+ <script>
85
+ document.getElementById('hover').addEventListener('mouseenter', () => {
86
+ document.body.setAttribute('data-hovered', '1');
87
+ });
88
+ </script>
89
+ </body>
90
+ `);
91
+ snapshot = await firefox.takeSnapshot();
92
+ const hoverUid = snapshot.json.root.children.find(n => n.tag === 'div')?.uid;
93
+ if (hoverUid) {
94
+ await firefox.hoverByUid(hoverUid);
95
+ await waitShort();
96
+ const hovered = await firefox.evaluate("return document.body.getAttribute('data-hovered')");
97
+ console.log(` ${hovered === '1' ? 'โœ…' : 'โŒ'} Hover: ${hovered}\n`);
98
+ } else {
99
+ console.log(' โŒ Div UID not found\n');
100
+ }
101
+
102
+ // Test 4: Fill Form
103
+ console.log('๐Ÿ“ Test 4: Fill Form By UID');
104
+ await loadHTML(firefox, `
105
+ <head><title>Test</title></head>
106
+ <body>
107
+ <input id="first" type="text" name="firstName">
108
+ <input id="last" type="text" name="lastName">
109
+ <script>
110
+ let values = {};
111
+ ['first', 'last'].forEach(id => {
112
+ document.getElementById(id).addEventListener('input', (e) => {
113
+ values[id] = e.target.value;
114
+ if (Object.keys(values).length === 2) {
115
+ document.body.setAttribute('data-form', JSON.stringify(values));
116
+ }
117
+ });
118
+ });
119
+ </script>
120
+ </body>
121
+ `);
122
+ snapshot = await firefox.takeSnapshot();
123
+ const inputs = snapshot.json.root.children.filter(n => n.tag === 'input');
124
+ if (inputs.length === 2) {
125
+ await firefox.fillFormByUid([
126
+ { uid: inputs[0].uid, value: 'John' },
127
+ { uid: inputs[1].uid, value: 'Doe' },
128
+ ]);
129
+ await waitShort(500);
130
+ const formData = await firefox.evaluate("return document.body.getAttribute('data-form')");
131
+ const parsed = JSON.parse(formData || '{}');
132
+ const ok = parsed.first === 'John' && parsed.last === 'Doe';
133
+ console.log(` ${ok ? 'โœ…' : 'โŒ'} Fill Form: ${formData}\n`);
134
+ } else {
135
+ console.log(` โŒ Expected 2 inputs, found ${inputs.length}\n`);
136
+ }
137
+
138
+ // Test 5: Upload File
139
+ console.log('๐Ÿ“ Test 5: Upload File By UID');
140
+ const tmpDir = await mkdtemp(join(tmpdir(), 'test-'));
141
+ const filePath = join(tmpDir, 'test.txt');
142
+ await writeFile(filePath, 'test content');
143
+
144
+ await loadHTML(firefox, `
145
+ <head><title>Test</title></head>
146
+ <body>
147
+ <input id="file" type="file" style="display:none">
148
+ <script>
149
+ document.getElementById('file').addEventListener('change', (e) => {
150
+ if (e.target.files[0]) {
151
+ document.body.setAttribute('data-filename', e.target.files[0].name);
152
+ }
153
+ });
154
+ </script>
155
+ </body>
156
+ `);
157
+ snapshot = await firefox.takeSnapshot();
158
+ const fileUid = snapshot.json.root.children.find(n => n.tag === 'input')?.uid;
159
+ if (fileUid) {
160
+ await firefox.uploadFileByUid(fileUid, filePath);
161
+ await waitShort();
162
+ const filename = await firefox.evaluate("return document.body.getAttribute('data-filename')");
163
+ console.log(` ${filename === 'test.txt' ? 'โœ…' : 'โŒ'} Upload: ${filename}\n`);
164
+ } else {
165
+ console.log(' โŒ File input UID not found\n');
166
+ }
167
+ await rm(tmpDir, { recursive: true, force: true });
168
+
169
+ // Test 6: Drag & Drop
170
+ console.log('๐Ÿงฒ Test 6: Drag & Drop By UID');
171
+ await loadHTML(firefox, `
172
+ <head><title>Test</title></head>
173
+ <body>
174
+ <div id="drag" draggable="true">Drag</div>
175
+ <div id="drop">Drop</div>
176
+ <script>
177
+ const drop = document.getElementById('drop');
178
+ drop.addEventListener('drop', (e) => {
179
+ e.preventDefault();
180
+ document.body.setAttribute('data-dropped', '1');
181
+ });
182
+ drop.addEventListener('dragover', (e) => e.preventDefault());
183
+ </script>
184
+ </body>
185
+ `);
186
+ snapshot = await firefox.takeSnapshot();
187
+ const divs = snapshot.json.root.children.filter(n => n.tag === 'div');
188
+ if (divs.length === 2) {
189
+ await firefox.dragByUidToUid(divs[0].uid, divs[1].uid);
190
+ await waitShort();
191
+ const dropped = await firefox.evaluate("return document.body.getAttribute('data-dropped')");
192
+ console.log(` ${dropped === '1' ? 'โœ…' : 'โŒ'} Drag & Drop: ${dropped}\n`);
193
+ } else {
194
+ console.log(` โŒ Expected 2 divs, found ${divs.length}\n`);
195
+ }
196
+
197
+ // Test 7: Double Click
198
+ console.log('๐Ÿ–ฑ๏ธ๐Ÿ–ฑ๏ธ Test 7: Double Click By UID');
199
+ await loadHTML(firefox, `
200
+ <head><title>Test</title></head>
201
+ <body>
202
+ <button id="dblBtn">Double Click</button>
203
+ <script>
204
+ document.getElementById('dblBtn').addEventListener('dblclick', () => {
205
+ document.body.setAttribute('data-dblclick', '1');
206
+ });
207
+ </script>
208
+ </body>
209
+ `);
210
+ snapshot = await firefox.takeSnapshot();
211
+ const dblBtnUid = snapshot.json.root.children.find(n => n.tag === 'button')?.uid;
212
+ if (dblBtnUid) {
213
+ await firefox.clickByUid(dblBtnUid, true);
214
+ await waitShort();
215
+ const dblClicked = await firefox.evaluate("return document.body.getAttribute('data-dblclick')");
216
+ console.log(` ${dblClicked === '1' ? 'โœ…' : 'โŒ'} Double Click: ${dblClicked}\n`);
217
+ } else {
218
+ console.log(' โŒ Button UID not found\n');
219
+ }
220
+
221
+ console.log('โœ… All tests completed! ๐ŸŽ‰\n');
222
+ } catch (error) {
223
+ console.error('โŒ Test failed:', error.message);
224
+ if (error.stack) console.error(error.stack);
225
+ process.exit(1);
226
+ } finally {
227
+ console.log('๐Ÿงน Closing...');
228
+ await firefox.close();
229
+ console.log('โœ… Done');
230
+ }
231
+ }
232
+
233
+ main().catch(console.error);
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Test lifecycle hooks and automatic cleanup on navigation
3
+ */
4
+ import { FirefoxDevTools } from '../dist/index.js';
5
+
6
+ async function testLifecycleHooks() {
7
+ console.log('๐Ÿงช Testing lifecycle hooks...\n');
8
+
9
+ const client = new FirefoxDevTools({
10
+ headless: true,
11
+ args: ['--width=1280', '--height=720'],
12
+ });
13
+
14
+ try {
15
+ await client.connect();
16
+ console.log('โœ… Connected to Firefox\n');
17
+
18
+ // Navigate to example.com
19
+ console.log('๐Ÿ“„ Navigating to example.com...');
20
+ await client.navigate('https://example.com');
21
+ console.log('โœ… Page loaded\n');
22
+
23
+ // Take snapshot and get a UID
24
+ console.log('๐Ÿ“ธ Taking snapshot...');
25
+ const snapshot1 = await client.takeSnapshot();
26
+ const uid1 = snapshot1.json.root.uid;
27
+ console.log(`โœ… Snapshot 1 taken, root UID: ${uid1}\n`);
28
+
29
+ // Verify UID works
30
+ console.log('๐Ÿงช Testing UID resolution before navigation...');
31
+ const selector1 = client.resolveUidToSelector(uid1);
32
+ console.log(`โœ… UID ${uid1} resolves to: ${selector1}\n`);
33
+
34
+ // Start network monitoring
35
+ console.log('๐ŸŒ Starting network monitoring...');
36
+ await client.startNetworkMonitoring();
37
+ console.log('โœ… Network monitoring started\n');
38
+
39
+ // Make some network requests by navigating
40
+ console.log('๐Ÿ“„ Navigating to mozilla.org (triggers lifecycle hooks)...');
41
+ await client.navigate('https://www.mozilla.org');
42
+ console.log('โœ… Navigation completed\n');
43
+
44
+ // Wait a bit for network requests
45
+ await new Promise((resolve) => setTimeout(resolve, 2000));
46
+
47
+ // Check network requests (should have some from mozilla.org)
48
+ const requests1 = await client.getNetworkRequests();
49
+ console.log(`๐Ÿ“Š Network requests captured: ${requests1.length}`);
50
+ if (requests1.length > 0) {
51
+ console.log(` First request: ${requests1[0].method} ${requests1[0].url}\n`);
52
+ }
53
+
54
+ // Test staleness: old UID should fail
55
+ console.log('๐Ÿงช Testing staleness detection (old UID should fail)...');
56
+ try {
57
+ client.resolveUidToSelector(uid1);
58
+ console.log('โŒ FAIL: Old UID should have been invalidated!\n');
59
+ } catch (err) {
60
+ console.log(`โœ… PASS: Old UID correctly rejected: ${err.message}\n`);
61
+ }
62
+
63
+ // Take new snapshot
64
+ console.log('๐Ÿ“ธ Taking new snapshot...');
65
+ const snapshot2 = await client.takeSnapshot();
66
+ const uid2 = snapshot2.json.root.uid;
67
+ console.log(`โœ… Snapshot 2 taken, root UID: ${uid2}\n`);
68
+
69
+ // Navigate again
70
+ console.log('๐Ÿ“„ Navigating to example.com again (triggers lifecycle hooks)...');
71
+ await client.navigate('https://example.com');
72
+ console.log('โœ… Navigation completed\n');
73
+
74
+ // Wait for page to settle
75
+ await new Promise((resolve) => setTimeout(resolve, 1000));
76
+
77
+ // Check network requests after navigation (should be cleared automatically)
78
+ const requests2 = await client.getNetworkRequests();
79
+ console.log(`๐Ÿ“Š Network requests after 2nd navigation: ${requests2.length}`);
80
+ console.log(
81
+ requests2.length === 0
82
+ ? 'โœ… PASS: Network requests auto-cleared on navigation\n'
83
+ : 'โš ๏ธ WARN: Network requests not cleared (check lifecycle hook)\n'
84
+ );
85
+
86
+ // Test console messages
87
+ console.log('๐Ÿ“ Checking console messages...');
88
+ const consoleMessages1 = await client.getConsoleMessages();
89
+ console.log(` Console messages: ${consoleMessages1.length}\n`);
90
+
91
+ // Trigger console message
92
+ await client.evaluate(`console.log('Test message from lifecycle hook test')`);
93
+ await new Promise((resolve) => setTimeout(resolve, 500));
94
+
95
+ const consoleMessages2 = await client.getConsoleMessages();
96
+ console.log(` Console messages after log: ${consoleMessages2.length}`);
97
+ console.log(
98
+ ` Last message: ${consoleMessages2[consoleMessages2.length - 1]?.text || 'none'}\n`
99
+ );
100
+
101
+ // Navigate again and check if console was cleared
102
+ console.log('๐Ÿ“„ Navigating one more time to check console clearing...');
103
+ await client.navigate('https://example.com');
104
+ await new Promise((resolve) => setTimeout(resolve, 1000));
105
+
106
+ const consoleMessages3 = await client.getConsoleMessages();
107
+ console.log(`๐Ÿ“Š Console messages after 3rd navigation: ${consoleMessages3.length}`);
108
+ console.log(
109
+ consoleMessages3.length === 0
110
+ ? 'โœ… PASS: Console auto-cleared on navigation\n'
111
+ : 'โš ๏ธ INFO: Console has messages (from new page)\n'
112
+ );
113
+
114
+ console.log('โœ… All lifecycle hook tests completed! ๐ŸŽ‰\n');
115
+ } catch (error) {
116
+ console.error('โŒ Test failed:', error);
117
+ } finally {
118
+ console.log('๐Ÿงน Closing connection...');
119
+ await client.close();
120
+ console.log('โœ… Done');
121
+ }
122
+ }
123
+
124
+ testLifecycleHooks();