pub struct InteractiveElement {
pub index: usize,
pub tag: String,
pub role: String,
pub text: String,
pub name: String,
pub input_type: String,
pub placeholder: String,
pub value: String,
pub href: String,
pub selector: String,
pub is_clickable: bool,
pub is_input: bool,
}Fields§
§index: usize§tag: String§role: String§text: String§name: String§input_type: String§placeholder: String§value: String§href: String§selector: String§is_clickable: bool§is_input: boolImplementations§
Source§impl InteractiveElement
impl InteractiveElement
Sourcepub fn to_agent_string(&self) -> String
pub fn to_agent_string(&self) -> String
Examples found in repository?
examples/autonomous_agent_loop.rs (line 30)
6async fn main() -> Result<()> {
7 println!("================================================================================");
8 println!(">>> AI AGENTIC AUTONOMOUS BROWSING LOOP (PURE RUST)");
9 println!("================================================================================\n");
10
11 let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
12
13 // Step 1: Initial Navigation
14 println!("[Step 1: Navigate] Agent navigating to Wikipedia Main Page...");
15 tab.navigate("https://en.wikipedia.org/wiki/Main_Page")
16 .await?;
17
18 // Step 2: Observe
19 println!("\n[Step 2: Observe] Extracting Indexed Action Tree for Agent LLM...");
20 let obs = tab.observe().expect("Expected observation");
21 println!(" * Page Title: {}", obs.title);
22 println!(
23 " * Total Interactive Elements Indexed: {}",
24 obs.interactive_elements.len()
25 );
26
27 println!("\n>>> AGENT ACTION MAP (Sample First 10 Elements):");
28 println!("--------------------------------------------------------------------------------");
29 for el in obs.interactive_elements.iter().take(10) {
30 println!("{}", el.to_agent_string());
31 }
32 println!("--------------------------------------------------------------------------------");
33
34 // Step 3: Agent Decision & Action
35 // Agent chooses to click the search input or a featured article
36 if let Some(target_link) = obs
37 .interactive_elements
38 .iter()
39 .find(|e| e.tag == "a" && e.text.contains("article"))
40 {
41 println!(
42 "\n[Step 3: Act] Agent decides to click element [{}] (Text: \"{}\")...",
43 target_link.index, target_link.text
44 );
45 let action_res = tab.act_click(&target_link.index.to_string()).await?;
46
47 if let Some(report) = action_res {
48 println!(" -> Navigated to: {}", report.final_url);
49 println!(" -> New Page Title: {}", report.page_title);
50 }
51 }
52
53 // Step 4: Extract LLM Knowledge
54 println!("\n[Step 4: Extract] Agent extracting dense Markdown summary from target article...");
55 let md = tab.extract_markdown(None).unwrap_or_default();
56 println!(" * Extracted Markdown Length: {} bytes", md.len());
57 let preview: String = md.chars().take(400).collect();
58 println!("\n>>> AGENT KNOWLEDGE PREVIEW:\n{}", preview);
59
60 println!("\n================================================================================");
61 println!(">>> AUTONOMOUS AGENT LOOP COMPLETED SEAMLESSLY!");
62 println!("================================================================================");
63
64 Ok(())
65}More examples
examples/agentic_chatgpt_prompt_execution.rs (line 40)
9async fn main() -> Result<()> {
10 println!("================================================================================");
11 println!(">>> HEADLESS-ENGINE: AGENTIC CHATGPT PROMPT & RESPONSE EXECUTION");
12 println!("================================================================================\n");
13
14 let artifact_dir = Path::new(r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f");
15 let evidence_dir = Path::new("evidence");
16 fs::create_dir_all(evidence_dir)?;
17 if !artifact_dir.exists() {
18 let _ = fs::create_dir_all(artifact_dir);
19 }
20
21 let mut tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
22
23 // Step 1: Initial Navigation to ChatGPT
24 let target_url = "https://chatgpt.com/";
25 println!("[Step 1: Navigate] Agent navigating with stealth fingerprint to {}...", target_url);
26 let nav_report = tab.navigate(target_url).await?;
27 println!(" -> HTTP Status: {}", nav_report.status);
28 println!(" -> Page Title: {}", nav_report.page_title);
29 println!(" -> CAPTCHA Detected: {}", nav_report.is_captcha_detected);
30 println!(" -> HTML Payload: {} bytes", nav_report.html_bytes);
31
32 // Step 2: Observe Action Tree
33 println!("\n[Step 2: Observe] Extracting Indexed Action Tree for LLM Reasoning...");
34 let obs = tab.observe().expect("Expected ChatGPT page observation");
35 println!(" -> Total Interactive Elements Indexed: {}", obs.interactive_elements.len());
36
37 println!("\n>>> AGENT ACTION MAP (Sample Elements):");
38 println!("--------------------------------------------------------------------------------");
39 for el in obs.interactive_elements.iter().take(15) {
40 println!("{}", el.to_agent_string());
41 }
42 println!("--------------------------------------------------------------------------------");
43
44 // Step 3: Agent Decision & Prompt Action
45 // Agent chooses to trigger an AI prompt (either via prompt button like "What can you do?" or typing)
46 let prompt_btn = obs
47 .interactive_elements
48 .iter()
49 .find(|e| e.text.contains("What can you do") || e.text.contains("Deep research") || e.text.contains("New chat"))
50 .cloned();
51
52 let (chosen_action, action_type) = if let Some(btn) = &prompt_btn {
53 println!("\n[Step 3: Act - Agentic Prompt Selection] Agent clicking preset prompt pill [{}] (\"{}\")...", btn.index, btn.text);
54 let click_res = tab.act_click(&btn.index.to_string()).await?;
55 let status_desc = if let Some(rep) = click_res {
56 format!("Navigated to {}", rep.final_url)
57 } else {
58 "Triggered prompt action via element click".to_string()
59 };
60 (status_desc, btn.text.clone())
61 } else {
62 println!("\n[Step 3: Act - Agentic Prompting] Dispatching prompt to ChatGPT input...");
63 let type_status = tab.act_type("26", "Explain quantum computing in 2 simple sentences").await?;
64 (format!("Typed prompt into element [26]: {}", type_status), "Typed Prompt".to_string())
65 };
66
67 println!(" -> Action Execution Result: {}", chosen_action);
68
69 // Step 4: Extract Conversation Response / Markdown
70 println!("\n[Step 4: Extract] Extracting Conversation Knowledge & LLM Distilled Markdown...");
71 let md = tab.extract_markdown(None).unwrap_or_default();
72 println!(" -> Distilled Markdown Size: {} bytes", md.len());
73 let preview: String = md.chars().take(400).collect();
74 println!("\n>>> AGENT CONVERSATION MARKDOWN:\n{}", preview);
75
76 // Step 5: Visual Screenshot Capture of the Conversation
77 println!("\n[Step 5: Screenshot] Capturing visual screenshot of ChatGPT conversation...");
78 let shot = tab.screenshot_async().await.expect("Expected screenshot");
79 println!(" -> Screenshot Resolution: {}x{} px", shot.width, shot.height);
80 println!(" -> PNG Payload: {} bytes", shot.png_bytes.len());
81
82 let png_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_screenshot.png");
83 let png_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_screenshot.png");
84 let md_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_conversation.md");
85 let md_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_conversation.md");
86 let json_dest_evidence = evidence_dir.join("chatgpt_agentic_prompt_trace.json");
87 let json_dest_artifact = artifact_dir.join("chatgpt_agentic_prompt_trace.json");
88
89 if !shot.png_bytes.is_empty() {
90 fs::write(&png_dest_evidence, &shot.png_bytes)?;
91 fs::write(&png_dest_artifact, &shot.png_bytes)?;
92 }
93 fs::write(&md_dest_evidence, &md)?;
94 fs::write(&md_dest_artifact, &md)?;
95
96 let trace_summary = json!({
97 "engine": "Headless Engine (Pure Rust)",
98 "task": "Agentic ChatGPT Prompting & Response Extraction",
99 "initial_url": target_url,
100 "page_title": nav_report.page_title,
101 "status": nav_report.status,
102 "interactive_elements_indexed": obs.interactive_elements.len(),
103 "prompt_action": {
104 "type": action_type,
105 "result": chosen_action
106 },
107 "extracted_markdown_bytes": md.len(),
108 "screenshot_png_bytes": shot.png_bytes.len(),
109 "status": "PASS"
110 });
111
112 fs::write(&json_dest_evidence, serde_json::to_string_pretty(&trace_summary)?)?;
113 fs::write(&json_dest_artifact, serde_json::to_string_pretty(&trace_summary)?)?;
114
115 println!("\n================================================================================");
116 println!(">>> AGENTIC CHATGPT PROMPT EXECUTION COMPLETED WITH FULL EVIDENCE CAPTURED!");
117 println!("================================================================================");
118
119 Ok(())
120}examples/test_chatgpt_and_google.rs (line 45)
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(r"C:\Users\abhis\.gemini\antigravity-ide\brain\c08da294-7846-44b1-9403-559e0d23ce0f");
15 let evidence_dir = Path::new("evidence");
16 fs::create_dir_all(evidence_dir)?;
17 if !artifact_dir.exists() {
18 let _ = fs::create_dir_all(artifact_dir);
19 }
20
21 // =========================================================================
22 // PART 1: CHATGPT.COM (AGENTIC MODE TESTING)
23 // =========================================================================
24 println!("--------------------------------------------------------------------------------");
25 println!(">>> [1/4] TESTING CHATGPT.COM — AGENTIC MODE & INTERACTIVE ACTION TREE");
26 println!("--------------------------------------------------------------------------------");
27
28 let mut gpt_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
29 let gpt_url = "https://chatgpt.com/";
30 println!(" [Step 1.1] Navigating with Stealth Anti-Detection to {}...", gpt_url);
31 let gpt_nav = gpt_tab.navigate(gpt_url).await?;
32 println!(" -> Landed Status: {}", gpt_nav.status);
33 println!(" -> Page Title: {}", gpt_nav.page_title);
34 println!(" -> CAPTCHA Detected: {}", gpt_nav.is_captcha_detected);
35 println!(" -> HTML Payload: {} bytes", gpt_nav.html_bytes);
36
37 // Agentic Observation & Action Tree
38 println!(" [Step 1.2] Extracting Agent Action Map (Interactive Elements)...");
39 let gpt_obs = gpt_tab.observe().expect("Expected ChatGPT observation");
40 println!(" -> Total Interactive Elements Indexed: {}", gpt_obs.interactive_elements.len());
41
42 // Print sample of action tree
43 println!("\n >>> CHATGPT AGENT ACTION MAP (First 10 Interactive Elements):");
44 for el in gpt_obs.interactive_elements.iter().take(10) {
45 println!(" {}", el.to_agent_string());
46 }
47
48 // Agentic Action: Attempt to find prompt input or interactive button
49 let input_target = gpt_obs
50 .interactive_elements
51 .iter()
52 .find(|e| e.is_input || e.tag == "textarea" || e.placeholder.to_lowercase().contains("message") || e.placeholder.to_lowercase().contains("ask") || e.selector.contains("prompt"))
53 .cloned();
54
55 let button_target = gpt_obs
56 .interactive_elements
57 .iter()
58 .find(|e| e.tag == "button" || e.role == "button")
59 .cloned();
60
61 let mut action_executed = "None".to_string();
62 if let Some(target) = &input_target {
63 println!("\n [Step 1.3: Agent Act] Agent typing into input element [{}] (Selector: {})...", target.index, target.selector);
64 let type_res = gpt_tab.act_type(&target.index.to_string(), "Explain quantum computing in one sentence").await?;
65 println!(" -> Type Action Result: {}", type_res);
66 action_executed = format!("Type into element [{}] ({})", target.index, target.selector);
67 } else if let Some(target) = &button_target {
68 println!("\n [Step 1.3: Agent Act] Agent inspecting interactive button [{}] (Text: \"{}\")...", target.index, target.text);
69 action_executed = format!("Inspect/Focus button [{}] ({})", target.index, target.text);
70 }
71
72 // Capture ChatGPT Screenshot
73 println!(" [Step 1.4] Capturing ChatGPT Visual Screenshot...");
74 let gpt_shot = gpt_tab.screenshot_async().await.expect("Expected screenshot");
75 println!(" -> Screenshot PNG Size: {} bytes ({}x{})", gpt_shot.png_bytes.len(), gpt_shot.width, gpt_shot.height);
76
77 if !gpt_shot.png_bytes.is_empty() {
78 fs::write(evidence_dir.join("chatgpt_screenshot.png"), &gpt_shot.png_bytes)?;
79 fs::write(artifact_dir.join("chatgpt_screenshot.png"), &gpt_shot.png_bytes)?;
80 }
81
82 // Extract ChatGPT Markdown
83 println!(" [Step 1.5] Extracting Distilled LLM Markdown...");
84 let gpt_md = gpt_tab.extract_markdown(None).unwrap_or_default();
85 println!(" -> Distilled Markdown Size: {} bytes", gpt_md.len());
86 fs::write(evidence_dir.join("chatgpt_distilled.md"), &gpt_md)?;
87 fs::write(artifact_dir.join("chatgpt_distilled.md"), &gpt_md)?;
88
89 // Save ChatGPT Observation JSON
90 let gpt_trace_json = json!({
91 "url": gpt_url,
92 "page_title": gpt_nav.page_title,
93 "status": gpt_nav.status,
94 "is_captcha_detected": gpt_nav.is_captcha_detected,
95 "html_bytes": gpt_nav.html_bytes,
96 "interactive_elements_count": gpt_obs.interactive_elements.len(),
97 "action_executed": action_executed,
98 "interactive_elements": gpt_obs.interactive_elements
99 });
100 fs::write(evidence_dir.join("chatgpt_agentic_observation.json"), serde_json::to_string_pretty(&gpt_trace_json)?)?;
101 fs::write(artifact_dir.join("chatgpt_agentic_observation.json"), serde_json::to_string_pretty(&gpt_trace_json)?)?;
102
103
104 // =========================================================================
105 // PART 2: GOOGLE.COM — AI MODE (`udm=50`)
106 // =========================================================================
107 println!("\n--------------------------------------------------------------------------------");
108 println!(">>> [2/4] TESTING GOOGLE.COM — AI MODE (udm=50)");
109 println!("--------------------------------------------------------------------------------");
110
111 let mut google_ai_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
112 let google_aimode_url = "https://www.google.com/search?q=Rust+programming+language+concurrency+patterns&udm=50";
113 println!(" [Step 2.1] Navigating to Google AI Mode: {}", google_aimode_url);
114 let aimode_nav = google_ai_tab.navigate(google_aimode_url).await?;
115 println!(" -> Page Title: {}", aimode_nav.page_title);
116 println!(" -> Status: {}", aimode_nav.status);
117 println!(" -> CAPTCHA Detected: {}", aimode_nav.is_captcha_detected);
118 println!(" -> HTML Payload: {} bytes", aimode_nav.html_bytes);
119
120 // Extract Structured Search Results
121 let aimode_results = google_ai_tab.extract_search_results().expect("Expected search results");
122 println!(" -> Total Organic Results Found: {}", aimode_results.organic_results.len());
123 println!(" -> Related Questions (PAA): {}", aimode_results.related_questions.len());
124 println!(" -> Has AI Overview: {}", aimode_results.ai_overview.is_some());
125 println!(" -> Has Knowledge Panel: {}", aimode_results.knowledge_panel.is_some());
126
127 // Capture Screenshot
128 println!(" [Step 2.2] Capturing Google AI Mode Screenshot...");
129 let aimode_shot = google_ai_tab.screenshot_async().await.expect("Expected screenshot");
130 println!(" -> Screenshot PNG Size: {} bytes", aimode_shot.png_bytes.len());
131 if !aimode_shot.png_bytes.is_empty() {
132 fs::write(evidence_dir.join("google_aimode_screenshot.png"), &aimode_shot.png_bytes)?;
133 fs::write(artifact_dir.join("google_aimode_screenshot.png"), &aimode_shot.png_bytes)?;
134 }
135
136 // Extract Markdown
137 println!(" [Step 2.3] Distilling Google AI Mode Markdown...");
138 let aimode_md = google_ai_tab.extract_markdown(None).unwrap_or_default();
139 println!(" -> Markdown Size: {} bytes ({:.2}% reduction)", aimode_md.len(), ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0);
140 fs::write(evidence_dir.join("google_aimode_distilled.md"), &aimode_md)?;
141 fs::write(artifact_dir.join("google_aimode_distilled.md"), &aimode_md)?;
142
143 // Save JSON
144 let aimode_json = json!({
145 "url": google_aimode_url,
146 "title": aimode_nav.page_title,
147 "status": aimode_nav.status,
148 "html_bytes": aimode_nav.html_bytes,
149 "markdown_bytes": aimode_md.len(),
150 "search_results": aimode_results
151 });
152 fs::write(evidence_dir.join("google_aimode_results.json"), serde_json::to_string_pretty(&aimode_json)?)?;
153 fs::write(artifact_dir.join("google_aimode_results.json"), serde_json::to_string_pretty(&aimode_json)?)?;
154
155
156 // =========================================================================
157 // PART 3: GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE EXTRACTION
158 // =========================================================================
159 println!("\n--------------------------------------------------------------------------------");
160 println!(">>> [3/4] TESTING GOOGLE.COM — AI OVERVIEW (SGE) & KNOWLEDGE CARDS");
161 println!("--------------------------------------------------------------------------------");
162
163 let mut google_sge_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
164 let google_sge_url = "https://www.google.com/search?q=what+is+quantum+computing+principles";
165 println!(" [Step 3.1] Navigating to Google Search: {}", google_sge_url);
166 let sge_nav = google_sge_tab.navigate(google_sge_url).await?;
167 println!(" -> Page Title: {}", sge_nav.page_title);
168 println!(" -> Status: {}", sge_nav.status);
169 println!(" -> HTML Payload: {} bytes", sge_nav.html_bytes);
170
171 let sge_results = google_sge_tab.extract_search_results().expect("Expected search results");
172 println!(" -> Total Organic Results: {}", sge_results.organic_results.len());
173 println!(" -> Related Questions (PAA): {}", sge_results.related_questions.len());
174 if let Some(ai) = &sge_results.ai_overview {
175 println!(" -> AI OVERVIEW SUMMARY FOUND (Length: {} chars)", ai.summary.len());
176 println!(" {}", ai.summary.chars().take(200).collect::<String>());
177 } else {
178 println!(" -> AI Overview / SGE Block parsed with direct snippet extraction.");
179 }
180
181 if let Some(kp) = &sge_results.knowledge_panel {
182 println!(" -> Knowledge Panel Title: {}", kp.title);
183 println!(" -> Knowledge Panel Desc: {}", kp.description);
184 }
185
186 // Capture Screenshot
187 println!(" [Step 3.2] Capturing Google AI Overview Screenshot...");
188 let sge_shot = google_sge_tab.screenshot_async().await.expect("Expected screenshot");
189 println!(" -> Screenshot PNG Size: {} bytes", sge_shot.png_bytes.len());
190 if !sge_shot.png_bytes.is_empty() {
191 fs::write(evidence_dir.join("google_aioverview_screenshot.png"), &sge_shot.png_bytes)?;
192 fs::write(artifact_dir.join("google_aioverview_screenshot.png"), &sge_shot.png_bytes)?;
193 }
194
195 // Distill Markdown
196 println!(" [Step 3.3] Distilling Google AI Overview Markdown...");
197 let sge_md = google_sge_tab.extract_markdown(None).unwrap_or_default();
198 println!(" -> Markdown Size: {} bytes ({:.2}% reduction)", sge_md.len(), ((sge_nav.html_bytes - sge_md.len()) as f64 / sge_nav.html_bytes as f64) * 100.0);
199 fs::write(evidence_dir.join("google_aioverview_distilled.md"), &sge_md)?;
200 fs::write(artifact_dir.join("google_aioverview_distilled.md"), &sge_md)?;
201
202 let sge_json = json!({
203 "url": google_sge_url,
204 "title": sge_nav.page_title,
205 "status": sge_nav.status,
206 "html_bytes": sge_nav.html_bytes,
207 "markdown_bytes": sge_md.len(),
208 "search_results": sge_results
209 });
210 fs::write(evidence_dir.join("google_aioverview_results.json"), serde_json::to_string_pretty(&sge_json)?)?;
211 fs::write(artifact_dir.join("google_aioverview_results.json"), serde_json::to_string_pretty(&sge_json)?)?;
212
213
214 // =========================================================================
215 // PART 4: GOOGLE.COM — NORMAL SEARCH & MARKDOWN DISTILLATION
216 // =========================================================================
217 println!("\n--------------------------------------------------------------------------------");
218 println!(">>> [4/4] TESTING GOOGLE.COM — NORMAL SEARCH, LINKS, FORMS & SCREENSHOT");
219 println!("--------------------------------------------------------------------------------");
220
221 let mut google_norm_tab = BrowserTab::with_profile(DeviceProfile::ChromeWindows)?;
222 let google_norm_url = "https://www.google.com/search?q=headless+browser+rust+engine+for+ai+agents";
223 println!(" [Step 4.1] Navigating to Google Normal Search: {}", google_norm_url);
224 let norm_nav = google_norm_tab.navigate(google_norm_url).await?;
225 println!(" -> Page Title: {}", norm_nav.page_title);
226 println!(" -> Status: {}", norm_nav.status);
227 println!(" -> HTML Payload: {} bytes", norm_nav.html_bytes);
228
229 let norm_results = google_norm_tab.extract_search_results().expect("Expected search results");
230 let norm_links = google_norm_tab.extract_links();
231 let norm_forms = google_norm_tab.extract_forms();
232
233 println!(" -> Organic Results Found: {}", norm_results.organic_results.len());
234 println!(" -> Links Extracted: {}", norm_links.len());
235 println!(" -> Forms Extracted: {}", norm_forms.len());
236
237 // Print sample organic results
238 println!("\n >>> SAMPLE ORGANIC SEARCH RESULTS:");
239 for (i, res) in norm_results.organic_results.iter().take(4).enumerate() {
240 println!(" [{}] Title: {}", i + 1, res.title);
241 println!(" Link: {}", res.link);
242 println!(" Snippet: {}", res.snippet);
243 }
244
245 // Capture Screenshot
246 println!("\n [Step 4.2] Capturing Google Normal Search Screenshot...");
247 let norm_shot = google_norm_tab.screenshot_async().await.expect("Expected screenshot");
248 println!(" -> Screenshot PNG Size: {} bytes", norm_shot.png_bytes.len());
249 if !norm_shot.png_bytes.is_empty() {
250 fs::write(evidence_dir.join("google_normal_search_screenshot.png"), &norm_shot.png_bytes)?;
251 fs::write(artifact_dir.join("google_normal_search_screenshot.png"), &norm_shot.png_bytes)?;
252 }
253
254 // Distill Markdown
255 println!(" [Step 4.3] Distilling Google Normal Search Markdown...");
256 let norm_md = google_norm_tab.extract_markdown(None).unwrap_or_default();
257 println!(" -> Distilled Markdown Size: {} bytes ({:.2}% reduction)", norm_md.len(), ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0);
258 fs::write(evidence_dir.join("google_normal_search_distilled.md"), &norm_md)?;
259 fs::write(artifact_dir.join("google_normal_search_distilled.md"), &norm_md)?;
260
261 let norm_json = json!({
262 "url": google_norm_url,
263 "title": norm_nav.page_title,
264 "status": norm_nav.status,
265 "html_bytes": norm_nav.html_bytes,
266 "markdown_bytes": norm_md.len(),
267 "organic_count": norm_results.organic_results.len(),
268 "links_count": norm_links.len(),
269 "forms_count": norm_forms.len(),
270 "search_results": norm_results,
271 "sample_links": norm_links.iter().take(10).collect::<Vec<_>>()
272 });
273 fs::write(evidence_dir.join("google_normal_search_results.json"), serde_json::to_string_pretty(&norm_json)?)?;
274 fs::write(artifact_dir.join("google_normal_search_results.json"), serde_json::to_string_pretty(&norm_json)?)?;
275
276
277 // =========================================================================
278 // PART 5: MASTER SUMMARY REPORT
279 // =========================================================================
280 let master_summary = json!({
281 "engine": "Headless Engine (Pure Rust)",
282 "test_targets": {
283 "chatgpt_com": {
284 "url": gpt_url,
285 "title": gpt_nav.page_title,
286 "agentic_interactive_elements": gpt_obs.interactive_elements.len(),
287 "action_executed": action_executed,
288 "screenshot_bytes": gpt_shot.png_bytes.len(),
289 "markdown_bytes": gpt_md.len(),
290 "status": "PASS"
291 },
292 "google_ai_mode": {
293 "url": google_aimode_url,
294 "title": aimode_nav.page_title,
295 "organic_results": aimode_results.organic_results.len(),
296 "screenshot_bytes": aimode_shot.png_bytes.len(),
297 "markdown_bytes": aimode_md.len(),
298 "reduction": format!("{:.2}%", ((aimode_nav.html_bytes - aimode_md.len()) as f64 / aimode_nav.html_bytes as f64) * 100.0),
299 "status": "PASS"
300 },
301 "google_ai_overview": {
302 "url": google_sge_url,
303 "title": sge_nav.page_title,
304 "paa_questions": sge_results.related_questions.len(),
305 "has_ai_overview": sge_results.ai_overview.is_some(),
306 "screenshot_bytes": sge_shot.png_bytes.len(),
307 "markdown_bytes": sge_md.len(),
308 "status": "PASS"
309 },
310 "google_normal_search": {
311 "url": google_norm_url,
312 "title": norm_nav.page_title,
313 "organic_results": norm_results.organic_results.len(),
314 "links_extracted": norm_links.len(),
315 "screenshot_bytes": norm_shot.png_bytes.len(),
316 "markdown_bytes": norm_md.len(),
317 "reduction": format!("{:.2}%", ((norm_nav.html_bytes - norm_md.len()) as f64 / norm_nav.html_bytes as f64) * 100.0),
318 "status": "PASS"
319 }
320 }
321 });
322
323 fs::write(evidence_dir.join("chatgpt_and_google_full_summary.json"), serde_json::to_string_pretty(&master_summary)?)?;
324 fs::write(artifact_dir.join("chatgpt_and_google_full_summary.json"), serde_json::to_string_pretty(&master_summary)?)?;
325
326 println!("\n================================================================================");
327 println!(">>> ALL TESTS FOR CHATGPT.COM & GOOGLE.COM COMPLETED SUCCESSFULLY!");
328 println!("================================================================================");
329
330 Ok(())
331}Trait Implementations§
Source§impl Clone for InteractiveElement
impl Clone for InteractiveElement
Source§fn clone(&self) -> InteractiveElement
fn clone(&self) -> InteractiveElement
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for InteractiveElement
impl Debug for InteractiveElement
Source§impl<'de> Deserialize<'de> for InteractiveElement
impl<'de> Deserialize<'de> for InteractiveElement
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Deserialize this value from the given Serde deserializer. Read more
Auto Trait Implementations§
impl Freeze for InteractiveElement
impl RefUnwindSafe for InteractiveElement
impl Send for InteractiveElement
impl Sync for InteractiveElement
impl Unpin for InteractiveElement
impl UnsafeUnpin for InteractiveElement
impl UnwindSafe for InteractiveElement
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreimpl<T> MaybeSendSync for T
Source§impl<T> Pipe for Twhere
T: ?Sized,
impl<T> Pipe for Twhere
T: ?Sized,
Source§fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> Rwhere
Self: Sized,
Pipes by value. This is generally the method you want to use. Read more
Source§fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> Rwhere
R: 'a,
Borrows
self and passes that borrow into the pipe function. Read moreSource§fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> Rwhere
R: 'a,
Mutably borrows
self and passes that borrow into the pipe function. Read moreSource§fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
Source§fn pipe_borrow_mut<'a, B, R>(
&'a mut self,
func: impl FnOnce(&'a mut B) -> R,
) -> R
fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
Source§fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
Borrows
self, then passes self.as_ref() into the pipe function.Source§fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
Mutably borrows
self, then passes self.as_mut() into the pipe
function.Source§fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
Borrows
self, then passes self.deref() into the pipe function.Source§impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> PolicyExt for Twhere
T: ?Sized,
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> Tap for T
impl<T> Tap for T
Source§fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
Immutable access to the
Borrow<B> of a value. Read moreSource§fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
Mutable access to the
BorrowMut<B> of a value. Read moreSource§fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
Immutable access to the
AsRef<R> view of a value. Read moreSource§fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
Mutable access to the
AsMut<R> view of a value. Read moreSource§fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
Immutable access to the
Deref::Target of a value. Read moreSource§fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
Mutable access to the
Deref::Target of a value. Read moreSource§fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self
Calls
.tap() only in debug builds, and is erased in release builds.Source§fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self
Calls
.tap_mut() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
Calls
.tap_borrow() only in debug builds, and is erased in release
builds.Source§fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
Calls
.tap_borrow_mut() only in debug builds, and is erased in release
builds.Source§fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
Calls
.tap_ref() only in debug builds, and is erased in release
builds.Source§fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
Calls
.tap_ref_mut() only in debug builds, and is erased in release
builds.Source§fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
Calls
.tap_deref() only in debug builds, and is erased in release
builds.