1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
//! Tests for parent navigation functionality
//!
//! This module tests the parent() method implementation across different platforms,
//! ensuring that parent-child relationships are correctly navigated in the UI tree.
use crate::{AutomationError, Desktop, UIElement};
use std::time::Duration;
/// Test fixture for parent navigation tests that ensures proper setup and cleanup
struct ParentTestFixture {
#[allow(dead_code)]
desktop: Desktop,
app: Option<UIElement>,
}
impl ParentTestFixture {
/// Creates a new test fixture with Notepad opened for reliable testing
fn new() -> Result<Self, AutomationError> {
let desktop = Desktop::new(false, false)?;
// Open Notepad as a simple, reliable test application
let app = desktop.open_application("notepad").ok();
// Wait for the application to be ready
std::thread::sleep(Duration::from_millis(1000));
Ok(Self { desktop, app })
}
}
impl Drop for ParentTestFixture {
fn drop(&mut self) {
// Clean up: close the application if it was opened
if let Some(ref app) = self.app {
let _ = app.close();
}
// Small delay to ensure cleanup completes
std::thread::sleep(Duration::from_millis(100));
}
}
#[test]
#[cfg(target_os = "windows")]
fn test_parent_navigation_basic() {
println!("\nđ Testing basic parent navigation functionality");
let fixture = ParentTestFixture::new().expect("Failed to create test fixture");
if let Some(ref app) = fixture.app {
// Find a child element (text editor area) in Notepad
match app.children() {
Ok(children) => {
let mut found_valid_parent = false;
for child in children.iter().take(5) {
// Test parent navigation
match child.parent() {
Ok(Some(parent)) => {
println!(" â
Found parent for child element");
// Verify the parent is actually different from the child
let child_id = child.id().unwrap_or_default();
let parent_id = parent.id().unwrap_or_default();
assert_ne!(
child_id, parent_id,
"Parent should have different ID than child"
);
// Verify parent has the child in its children list
if let Ok(parent_children) = parent.children() {
let child_found_in_parent = parent_children
.iter()
.any(|pc| pc.id().unwrap_or_default() == child_id);
if child_found_in_parent {
println!(
" â
Verified parent-child relationship is bidirectional"
);
found_valid_parent = true;
break;
}
}
}
Ok(None) => {
println!(" âšī¸ Element has no parent (might be root element)");
}
Err(e) => {
println!(" â ī¸ Error getting parent: {e}");
}
}
}
assert!(
found_valid_parent,
"Should find at least one element with a valid parent"
);
}
Err(e) => panic!("Failed to get children from app: {e}"),
}
} else {
panic!("Failed to open Notepad for testing");
}
}
#[test]
#[cfg(target_os = "windows")]
fn test_parent_navigation_multi_level() {
println!("\nđ Testing multi-level parent navigation (grandparents)");
let fixture = ParentTestFixture::new().expect("Failed to create test fixture");
if let Some(ref app) = fixture.app {
// Find a deeply nested element
if let Ok(children) = app.children() {
for child in children.iter().take(3) {
if let Ok(grandchildren) = child.children() {
for grandchild in grandchildren.iter().take(2) {
// Test parent navigation: grandchild -> child
match grandchild.parent() {
Ok(Some(parent)) => {
println!(" â
Grandchild found its parent");
// Test grandparent navigation: parent -> grandparent
match parent.parent() {
Ok(Some(grandparent)) => {
println!(
" â
Found grandparent through parent navigation"
);
// Verify IDs are all different
let grandchild_id = grandchild.id().unwrap_or_default();
let parent_id = parent.id().unwrap_or_default();
let grandparent_id = grandparent.id().unwrap_or_default();
assert_ne!(
grandchild_id, parent_id,
"Grandchild and parent should have different IDs"
);
assert_ne!(
parent_id, grandparent_id,
"Parent and grandparent should have different IDs"
);
assert_ne!(
grandchild_id, grandparent_id,
"Grandchild and grandparent should have different IDs"
);
println!(" â
Multi-level parent navigation test passed");
return;
}
Ok(None) => {
println!(" âšī¸ Parent has no parent (reached root)");
}
Err(e) => {
println!(" â ī¸ Error getting grandparent: {e}");
}
}
}
Ok(None) => {
println!(" âšī¸ Grandchild has no parent");
}
Err(e) => {
println!(" â ī¸ Error getting parent from grandchild: {e}");
}
}
}
}
}
}
println!(
" âšī¸ Multi-level navigation completed (may not have found deeply nested elements)"
);
} else {
panic!("Failed to open Notepad for testing");
}
}
#[test]
#[cfg(target_os = "windows")]
fn test_parent_navigation_error_handling() {
println!("\nđ Testing parent navigation error handling");
let fixture = ParentTestFixture::new().expect("Failed to create test fixture");
if let Some(ref app) = fixture.app {
// Test with the root application element - should handle gracefully
match app.parent() {
Ok(Some(_parent)) => {
println!(" â
Root element has a parent (window manager or desktop)");
}
Ok(None) => {
println!(" â
Root element correctly reports no parent");
}
Err(e) => {
println!(" â
Parent navigation error handled gracefully: {e}");
// This is acceptable behavior - the error should be an AutomationError::ElementNotFound
assert!(
matches!(e, AutomationError::ElementNotFound(_)),
"Should return ElementNotFound error, got: {e:?}"
);
}
}
// Test with a child element that should have a parent
if let Ok(children) = app.children() {
if let Some(child) = children.first() {
match child.parent() {
Ok(Some(_parent)) => {
println!(" â
Child element successfully found its parent");
}
Ok(None) => {
println!(" â ī¸ Child element reports no parent (unexpected)");
}
Err(e) => {
println!(" â ī¸ Error getting parent from child: {e}");
// This should generally not happen for valid child elements
}
}
}
}
} else {
panic!("Failed to open Notepad for testing");
}
}
#[test]
#[cfg(target_os = "windows")]
fn test_parent_navigation_consistency() {
println!("\nđ Testing parent navigation consistency");
let fixture = ParentTestFixture::new().expect("Failed to create test fixture");
if let Some(ref app) = fixture.app {
if let Ok(children) = app.children() {
for (i, child) in children.iter().take(3).enumerate() {
println!(" đ Testing child element #{}", i + 1);
// Get parent multiple times and ensure consistency
let parent1 = child.parent();
let parent2 = child.parent();
match (parent1, parent2) {
(Ok(Some(p1)), Ok(Some(p2))) => {
let id1 = p1.id().unwrap_or_default();
let id2 = p2.id().unwrap_or_default();
assert_eq!(
id1, id2,
"Multiple calls to parent() should return the same element"
);
println!(" â
Parent navigation is consistent");
}
(Ok(None), Ok(None)) => {
println!(" â
Consistently reports no parent");
}
(Err(_), Err(_)) => {
println!(" â
Consistently reports error");
}
_ => {
panic!("Inconsistent parent() results between calls");
}
}
}
}
} else {
panic!("Failed to open Notepad for testing");
}
}