1use anyhow::Result;
2use headless_engine::browser::tab::BrowserTab;
3use headless_engine::network::fingerprint::DeviceProfile;
4use serde_json::json;
5use std::fs;
6use std::path::Path;
7
8#[tokio::main]
9async fn main() -> Result<()> {
10 println!("================================================================================");
11 println!(">>> TEST SUITE: CHATGPT.COM (AGENTIC MODE) & GOOGLE.COM (AI MODE, AI OVERVIEW, NORMAL SEARCH)");
12 println!("================================================================================\n");
13
14 let artifact_dir = Path::new(
15 r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f",
16 );
17 let evidence_dir = Path::new("evidence");
18 fs::create_dir_all(evidence_dir)?;
19 if !artifact_dir.exists() {
20 let _ = fs::create_dir_all(artifact_dir);
21 }
22
23 println!("--------------------------------------------------------------------------------");
27 println!(">>> [1/4] TESTING CHATGPT.COM — AGENTIC MODE & INTERACTIVE ACTION TREE");
28 println!("--------------------------------------------------------------------------------");
29
30 let mut gpt_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
31 let gpt_url = "https://chatgpt.com/";
32 println!(
33 " [Step 1.1] Navigating with Stealth Anti-Detection to {}...",
34 gpt_url
35 );
36 let gpt_nav = gpt_tab.navigate(gpt_url).await?;
37 println!(" -> Landed Status: {}", gpt_nav.status);
38 println!(" -> Page Title: {}", gpt_nav.page_title);
39 println!(" -> CAPTCHA Detected: {}", gpt_nav.is_captcha_detected);
40 println!(" -> HTML Payload: {} bytes", gpt_nav.html_bytes);
41
42 println!(" [Step 1.2] Extracting Agent Action Map (Interactive Elements)...");
44 let gpt_obs = gpt_tab.observe().expect("Expected ChatGPT observation");
45 println!(
46 " -> Total Interactive Elements Indexed: {}",
47 gpt_obs.interactive_elements.len()
48 );
49
50 println!("\n >>> CHATGPT AGENT ACTION MAP (First 10 Interactive Elements):");
52 for el in gpt_obs.interactive_elements.iter().take(10) {
53 println!(" {}", el.to_agent_string());
54 }
55
56 let input_target = gpt_obs
58 .interactive_elements
59 .iter()
60 .find(|e| {
61 e.is_input
62 || e.tag == "textarea"
63 || e.placeholder.to_lowercase().contains("message")
64 || e.placeholder.to_lowercase().contains("ask")
65 || e.selector.contains("prompt")
66 })
67 .cloned();
68
69 let button_target = gpt_obs
70 .interactive_elements
71 .iter()
72 .find(|e| e.tag == "button" || e.role == "button")
73 .cloned();
74
75 let mut action_executed = "None".to_string();
76 if let Some(target) = &input_target {
77 println!(
78 "\n [Step 1.3: Agent Act] Agent typing into input element [{}] (Selector: {})...",
79 target.index, target.selector
80 );
81 let type_res = gpt_tab
82 .act_type(
83 &target.index.to_string(),
84 "Explain quantum computing in one sentence",
85 )
86 .await?;
87 println!(" -> Type Action Result: {}", type_res);
88 action_executed = format!("Type into element [{}] ({})", target.index, target.selector);
89 } else if let Some(target) = &button_target {
90 println!(
91 "\n [Step 1.3: Agent Act] Agent inspecting interactive button [{}] (Text: \"{}\")...",
92 target.index, target.text
93 );
94 action_executed = format!("Inspect/Focus button [{}] ({})", target.index, target.text);
95 }
96
97 println!(" [Step 1.4] Capturing ChatGPT Visual Screenshot...");
99 let gpt_shot = gpt_tab
100 .screenshot_async()
101 .await
102 .expect("Expected screenshot");
103 println!(
104 " -> Screenshot PNG Size: {} bytes ({}x{})",
105 gpt_shot.png_bytes.len(),
106 gpt_shot.width,
107 gpt_shot.height
108 );
109
110 if !gpt_shot.png_bytes.is_empty() {
111 fs::write(
112 evidence_dir.join("chatgpt_screenshot.png"),
113 &gpt_shot.png_bytes,
114 )?;
115 fs::write(
116 artifact_dir.join("chatgpt_screenshot.png"),
117 &gpt_shot.png_bytes,
118 )?;
119 }
120
121 println!(" [Step 1.5] Extracting Distilled LLM Markdown...");
123 let gpt_md = gpt_tab.extract_markdown(None).unwrap_or_default();
124 println!(" -> Distilled Markdown Size: {} bytes", gpt_md.len());
125 fs::write(evidence_dir.join("chatgpt_distilled.md"), &gpt_md)?;
126 fs::write(artifact_dir.join("chatgpt_distilled.md"), &gpt_md)?;
127
128 let gpt_trace_json = json!({
130 "url": gpt_url,
131 "page_title": gpt_nav.page_title,
132 "status": gpt_nav.status,
133 "is_captcha_detected": gpt_nav.is_captcha_detected,
134 "html_bytes": gpt_nav.html_bytes,
135 "interactive_elements_count": gpt_obs.interactive_elements.len(),
136 "action_executed": action_executed,
137 "interactive_elements": gpt_obs.interactive_elements
138 });
139 fs::write(
140 evidence_dir.join("chatgpt_agentic_observation.json"),
141 serde_json::to_string_pretty(&gpt_trace_json)?,
142 )?;
143 fs::write(
144 artifact_dir.join("chatgpt_agentic_observation.json"),
145 serde_json::to_string_pretty(&gpt_trace_json)?,
146 )?;
147
148 println!("\n--------------------------------------------------------------------------------");
152 println!(">>> [2/4] TESTING GOOGLE.COM — AI MODE (udm=50)");
153 println!("--------------------------------------------------------------------------------");
154
155 let mut google_ai_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
156 let google_aimode_url =
157 "https://www.google.com/search?q=Rust+programming+language+concurrency+patterns&udm=50";
158 println!(
159 " [Step 2.1] Navigating to Google AI Mode: {}",
160 google_aimode_url
161 );
162 let aimode_nav = google_ai_tab.navigate(google_aimode_url).await?;
163 println!(" -> Page Title: {}", aimode_nav.page_title);
164 println!(" -> Status: {}", aimode_nav.status);
165 println!(" -> CAPTCHA Detected: {}", aimode_nav.is_captcha_detected);
166 println!(" -> HTML Payload: {} bytes", aimode_nav.html_bytes);
167
168 let aimode_results = google_ai_tab
170 .extract_search_results()
171 .expect("Expected search results");
172 println!(
173 " -> Total Organic Results Found: {}",
174 aimode_results.organic_results.len()
175 );
176 println!(
177 " -> Related Questions (PAA): {}",
178 aimode_results.related_questions.len()
179 );
180 println!(
181 " -> Has AI Overview: {}",
182 aimode_results.ai_overview.is_some()
183 );
184 println!(
185 " -> Has Knowledge Panel: {}",
186 aimode_results.knowledge_panel.is_some()
187 );
188
189 println!(" [Step 2.2] Capturing Google AI Mode Screenshot...");
191 let aimode_shot = google_ai_tab
192 .screenshot_async()
193 .await
194 .expect("Expected screenshot");
195 println!(
196 " -> Screenshot PNG Size: {} bytes",
197 aimode_shot.png_bytes.len()
198 );
199 if !aimode_shot.png_bytes.is_empty() {
200 fs::write(
201 evidence_dir.join("google_aimode_screenshot.png"),
202 &aimode_shot.png_bytes,
203 )?;
204 fs::write(
205 artifact_dir.join("google_aimode_screenshot.png"),
206 &aimode_shot.png_bytes,
207 )?;
208 }
209
210 println!(" [Step 2.3] Distilling Google AI Mode Markdown...");
212 let aimode_md = google_ai_tab.extract_markdown(None).unwrap_or_default();
213 println!(
214 " -> Markdown Size: {} bytes ({:.2}% reduction)",
215 aimode_md.len(),
216 ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0
217 );
218 fs::write(evidence_dir.join("google_aimode_distilled.md"), &aimode_md)?;
219 fs::write(artifact_dir.join("google_aimode_distilled.md"), &aimode_md)?;
220
221 let aimode_json = json!({
223 "url": google_aimode_url,
224 "title": aimode_nav.page_title,
225 "status": aimode_nav.status,
226 "html_bytes": aimode_nav.html_bytes,
227 "markdown_bytes": aimode_md.len(),
228 "search_results": aimode_results
229 });
230 fs::write(
231 evidence_dir.join("google_aimode_results.json"),
232 serde_json::to_string_pretty(&aimode_json)?,
233 )?;
234 fs::write(
235 artifact_dir.join("google_aimode_results.json"),
236 serde_json::to_string_pretty(&aimode_json)?,
237 )?;
238
239 println!("\n--------------------------------------------------------------------------------");
243 println!(">>> [3/4] TESTING GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE CARDS");
244 println!("--------------------------------------------------------------------------------");
245
246 let mut google_sge_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
247 let google_sge_url = "https://www.google.com/search?q=what+is+quantum+computing+principles";
248 println!(
249 " [Step 3.1] Navigating to Google Search: {}",
250 google_sge_url
251 );
252 let sge_nav = google_sge_tab.navigate(google_sge_url).await?;
253 println!(" -> Page Title: {}", sge_nav.page_title);
254 println!(" -> Status: {}", sge_nav.status);
255 println!(" -> HTML Payload: {} bytes", sge_nav.html_bytes);
256
257 let sge_results = google_sge_tab
258 .extract_search_results()
259 .expect("Expected search results");
260 println!(
261 " -> Total Organic Results: {}",
262 sge_results.organic_results.len()
263 );
264 println!(
265 " -> Related Questions (PAA): {}",
266 sge_results.related_questions.len()
267 );
268 if let Some(ai) = &sge_results.ai_overview {
269 println!(
270 " -> AI OVERVIEW SUMMARY FOUND (Length: {} chars)",
271 ai.summary.len()
272 );
273 println!(" {}", ai.summary.chars().take(200).collect::<String>());
274 } else {
275 println!(" -> AI Overview / SGE Block parsed with direct snippet extraction.");
276 }
277
278 if let Some(kp) = &sge_results.knowledge_panel {
279 println!(" -> Knowledge Panel Title: {}", kp.title);
280 println!(" -> Knowledge Panel Desc: {}", kp.description);
281 }
282
283 println!(" [Step 3.2] Capturing Google AI Overview Screenshot...");
285 let sge_shot = google_sge_tab
286 .screenshot_async()
287 .await
288 .expect("Expected screenshot");
289 println!(
290 " -> Screenshot PNG Size: {} bytes",
291 sge_shot.png_bytes.len()
292 );
293 if !sge_shot.png_bytes.is_empty() {
294 fs::write(
295 evidence_dir.join("google_aioverview_screenshot.png"),
296 &sge_shot.png_bytes,
297 )?;
298 fs::write(
299 artifact_dir.join("google_aioverview_screenshot.png"),
300 &sge_shot.png_bytes,
301 )?;
302 }
303
304 println!(" [Step 3.3] Distilling Google AI Overview Markdown...");
306 let sge_md = google_sge_tab.extract_markdown(None).unwrap_or_default();
307 println!(
308 " -> Markdown Size: {} bytes ({:.2}% reduction)",
309 sge_md.len(),
310 ((sge_nav.html_bytes - sge_md.len()) as f64 / sge_nav.html_bytes as f64) * 100.0
311 );
312 fs::write(evidence_dir.join("google_aioverview_distilled.md"), &sge_md)?;
313 fs::write(artifact_dir.join("google_aioverview_distilled.md"), &sge_md)?;
314
315 let sge_json = json!({
316 "url": google_sge_url,
317 "title": sge_nav.page_title,
318 "status": sge_nav.status,
319 "html_bytes": sge_nav.html_bytes,
320 "markdown_bytes": sge_md.len(),
321 "search_results": sge_results
322 });
323 fs::write(
324 evidence_dir.join("google_aioverview_results.json"),
325 serde_json::to_string_pretty(&sge_json)?,
326 )?;
327 fs::write(
328 artifact_dir.join("google_aioverview_results.json"),
329 serde_json::to_string_pretty(&sge_json)?,
330 )?;
331
332 println!("\n--------------------------------------------------------------------------------");
336 println!(">>> [4/4] TESTING GOOGLE.COM — NORMAL SEARCH, LINKS, FORMS & SCREENSHOT");
337 println!("--------------------------------------------------------------------------------");
338
339 let mut google_norm_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
340 let google_norm_url =
341 "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
342 println!(
343 " [Step 4.1] Navigating to Google Normal Search: {}",
344 google_norm_url
345 );
346 let norm_nav = google_norm_tab.navigate(google_norm_url).await?;
347 println!(" -> Page Title: {}", norm_nav.page_title);
348 println!(" -> Status: {}", norm_nav.status);
349 println!(" -> HTML Payload: {} bytes", norm_nav.html_bytes);
350
351 let norm_results = google_norm_tab
352 .extract_search_results()
353 .expect("Expected search results");
354 let norm_links = google_norm_tab.extract_links();
355 let norm_forms = google_norm_tab.extract_forms();
356
357 println!(
358 " -> Organic Results Found: {}",
359 norm_results.organic_results.len()
360 );
361 println!(" -> Links Extracted: {}", norm_links.len());
362 println!(" -> Forms Extracted: {}", norm_forms.len());
363
364 println!("\n >>> SAMPLE ORGANIC SEARCH RESULTS:");
366 for (i, res) in norm_results.organic_results.iter().take(4).enumerate() {
367 println!(" [{}] Title: {}", i + 1, res.title);
368 println!(" Link: {}", res.link);
369 println!(" Snippet: {}", res.snippet);
370 }
371
372 println!("\n [Step 4.2] Capturing Google Normal Search Screenshot...");
374 let norm_shot = google_norm_tab
375 .screenshot_async()
376 .await
377 .expect("Expected screenshot");
378 println!(
379 " -> Screenshot PNG Size: {} bytes",
380 norm_shot.png_bytes.len()
381 );
382 if !norm_shot.png_bytes.is_empty() {
383 fs::write(
384 evidence_dir.join("google_normal_search_screenshot.png"),
385 &norm_shot.png_bytes,
386 )?;
387 fs::write(
388 artifact_dir.join("google_normal_search_screenshot.png"),
389 &norm_shot.png_bytes,
390 )?;
391 }
392
393 println!(" [Step 4.3] Distilling Google Normal Search Markdown...");
395 let norm_md = google_norm_tab.extract_markdown(None).unwrap_or_default();
396 println!(
397 " -> Distilled Markdown Size: {} bytes ({:.2}% reduction)",
398 norm_md.len(),
399 ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0
400 );
401 fs::write(
402 evidence_dir.join("google_normal_search_distilled.md"),
403 &norm_md,
404 )?;
405 fs::write(
406 artifact_dir.join("google_normal_search_distilled.md"),
407 &norm_md,
408 )?;
409
410 let norm_json = json!({
411 "url": google_norm_url,
412 "title": norm_nav.page_title,
413 "status": norm_nav.status,
414 "html_bytes": norm_nav.html_bytes,
415 "markdown_bytes": norm_md.len(),
416 "organic_count": norm_results.organic_results.len(),
417 "links_count": norm_links.len(),
418 "forms_count": norm_forms.len(),
419 "search_results": norm_results,
420 "sample_links": norm_links.iter().take(10).collect::<Vec<_>>()
421 });
422 fs::write(
423 evidence_dir.join("google_normal_search_results.json"),
424 serde_json::to_string_pretty(&norm_json)?,
425 )?;
426 fs::write(
427 artifact_dir.join("google_normal_search_results.json"),
428 serde_json::to_string_pretty(&norm_json)?,
429 )?;
430
431 let master_summary = json!({
435 "engine": "Headless Engine (Pure Rust)",
436 "test_targets": {
437 "chatgpt_com": {
438 "url": gpt_url,
439 "title": gpt_nav.page_title,
440 "agentic_interactive_elements": gpt_obs.interactive_elements.len(),
441 "action_executed": action_executed,
442 "screenshot_bytes": gpt_shot.png_bytes.len(),
443 "markdown_bytes": gpt_md.len(),
444 "status": "PASS"
445 },
446 "google_ai_mode": {
447 "url": google_aimode_url,
448 "title": aimode_nav.page_title,
449 "organic_results": aimode_results.organic_results.len(),
450 "screenshot_bytes": aimode_shot.png_bytes.len(),
451 "markdown_bytes": aimode_md.len(),
452 "reduction": format!("{:.2}%", ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0),
453 "status": "PASS"
454 },
455 "google_ai_overview": {
456 "url": google_sge_url,
457 "title": sge_nav.page_title,
458 "paa_questions": sge_results.related_questions.len(),
459 "has_ai_overview": sge_results.ai_overview.is_some(),
460 "screenshot_bytes": sge_shot.png_bytes.len(),
461 "markdown_bytes": sge_md.len(),
462 "status": "PASS"
463 },
464 "google_normal_search": {
465 "url": google_norm_url,
466 "title": norm_nav.page_title,
467 "organic_results": norm_results.organic_results.len(),
468 "links_extracted": norm_links.len(),
469 "screenshot_bytes": norm_shot.png_bytes.len(),
470 "markdown_bytes": norm_md.len(),
471 "reduction": format!("{:.2}%", ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0),
472 "status": "PASS"
473 }
474 }
475 });
476
477 fs::write(
478 evidence_dir.join("chatgpt_and_google_full_summary.json"),
479 serde_json::to_string_pretty(&master_summary)?,
480 )?;
481 fs::write(
482 artifact_dir.join("chatgpt_and_google_full_summary.json"),
483 serde_json::to_string_pretty(&master_summary)?,
484 )?;
485
486 println!("\n================================================================================");
487 println!(">>> ALL TESTS FOR CHATGPT.COM & GOOGLE.COM COMPLETED SUCCESSFULLY!");
488 println!("================================================================================");
489
490 Ok(())
491}