@elizaos/computeruse 0.24.20

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,243 @@
1
+ const { Desktop } = require("../index.js");
2
+
3
+ /**
4
+ * Cross-Application Verification Test Suite
5
+ * Tests verification methods across multiple Windows built-in applications
6
+ * These apps are available on all Windows 10/11 computers
7
+ */
8
+
9
+ const desktop = new Desktop();
10
+
11
+ // Test configuration for each app
12
+ const APP_TESTS = [
13
+ {
14
+ name: "Notepad",
15
+ launch: "notepad.exe",
16
+ existsSelector: "role:Document || role:Edit",
17
+ existsDescription: "text editor area",
18
+ menuBarSelector: "role:MenuBar",
19
+ notExistsSelector: "role:Window && name:Save As",
20
+ notExistsDescription: "Save As dialog",
21
+ required: true,
22
+ },
23
+ {
24
+ name: "Calculator",
25
+ launch: "calc",
26
+ existsSelector: "role:Button && name:Zero",
27
+ existsDescription: "Zero button",
28
+ menuBarSelector: null, // Calculator has no traditional menu bar
29
+ notExistsSelector: "role:Button && name:NonExistentCalcButton",
30
+ notExistsDescription: "non-existent button",
31
+ required: true,
32
+ },
33
+ {
34
+ name: "Google Chrome",
35
+ launch: "chrome",
36
+ existsSelector: "role:Document || role:Pane",
37
+ existsDescription: "browser content area",
38
+ menuBarSelector: null, // Chrome uses toolbar, not menu bar
39
+ notExistsSelector: "role:Window && name:Downloads Complete",
40
+ notExistsDescription: "Downloads Complete dialog",
41
+ required: false, // Chrome may not be installed
42
+ },
43
+ {
44
+ name: "Microsoft Edge",
45
+ launch: "msedge",
46
+ existsSelector: "role:Document || role:Pane",
47
+ existsDescription: "browser content area",
48
+ menuBarSelector: null, // Edge uses toolbar, not menu bar
49
+ notExistsSelector: "role:Window && name:Downloads Complete",
50
+ notExistsDescription: "Downloads Complete dialog",
51
+ required: false, // Edge can fail if already open or slow to launch
52
+ },
53
+ {
54
+ name: "File Explorer",
55
+ launch: "explorer",
56
+ existsSelector: "role:Pane",
57
+ existsDescription: "content pane",
58
+ menuBarSelector: null, // Modern Explorer uses ribbon
59
+ notExistsSelector: "role:Window && name:Confirm Delete",
60
+ notExistsDescription: "Confirm Delete dialog",
61
+ required: false, // Explorer has complex window structure, can be flaky
62
+ },
63
+ ];
64
+
65
+ async function closeApp(appElement, appName) {
66
+ try {
67
+ await appElement.pressKey("Alt+F4");
68
+ await new Promise(r => setTimeout(r, 500));
69
+
70
+ // Handle save dialogs if they appear
71
+ try {
72
+ const dontSave = await appElement.locator("role:Button && name:Don\\'t Save").first(1000);
73
+ await dontSave.click();
74
+ } catch {
75
+ // Try "No" button for some dialogs
76
+ try {
77
+ const noBtn = await appElement.locator("role:Button && name:No").first(500);
78
+ await noBtn.click();
79
+ } catch {
80
+ // No dialog, that's fine
81
+ }
82
+ }
83
+ } catch (e) {
84
+ console.log(` Note: Could not close ${appName} cleanly: ${e.message.substring(0, 50)}`);
85
+ }
86
+ }
87
+
88
+ async function testApp(config) {
89
+ console.log(`\n ═══ ${config.name} ═══`);
90
+
91
+ let appElement = null;
92
+ let passed = 0;
93
+ let failed = 0;
94
+ let skipped = false;
95
+
96
+ try {
97
+ // Launch app
98
+ console.log(` Launching ${config.name}...`);
99
+ try {
100
+ appElement = await desktop.openApplication(config.launch);
101
+ await new Promise(r => setTimeout(r, 1500)); // Wait for app to stabilize
102
+ } catch (launchError) {
103
+ if (!config.required) {
104
+ console.log(` ⏭️ Skipping ${config.name} (not installed or failed to launch)`);
105
+ return { passed: 0, failed: 0, skipped: true, name: config.name };
106
+ }
107
+ throw launchError;
108
+ }
109
+
110
+ // Test 1: verifyElementExists for known element
111
+ console.log(` Test 1: verifyElementExists (${config.existsDescription})`);
112
+ try {
113
+ const found = await desktop.verifyElementExists(
114
+ appElement,
115
+ config.existsSelector,
116
+ 5000
117
+ );
118
+ if (found) {
119
+ console.log(` ✅ Found ${config.existsDescription}: role=${found.role()}`);
120
+ passed++;
121
+ } else {
122
+ console.log(` ❌ Expected to find ${config.existsDescription}`);
123
+ failed++;
124
+ }
125
+ } catch (e) {
126
+ console.log(` ❌ Error: ${e.message.substring(0, 80)}`);
127
+ failed++;
128
+ }
129
+
130
+ // Test 2: verifyElementExists for MenuBar (if applicable)
131
+ if (config.menuBarSelector) {
132
+ console.log(` Test 2: verifyElementExists (MenuBar)`);
133
+ try {
134
+ const menuBar = await desktop.verifyElementExists(
135
+ appElement,
136
+ config.menuBarSelector,
137
+ 3000
138
+ );
139
+ if (menuBar) {
140
+ console.log(` ✅ Found MenuBar: role=${menuBar.role()}`);
141
+ passed++;
142
+ }
143
+ } catch (e) {
144
+ console.log(` ❌ MenuBar not found: ${e.message.substring(0, 60)}`);
145
+ failed++;
146
+ }
147
+ }
148
+
149
+ // Test 3: verifyElementNotExists for non-existent element
150
+ console.log(` Test 3: verifyElementNotExists (${config.notExistsDescription})`);
151
+ try {
152
+ await desktop.verifyElementNotExists(
153
+ appElement,
154
+ config.notExistsSelector,
155
+ 2000
156
+ );
157
+ console.log(` ✅ Correctly confirmed ${config.notExistsDescription} does not exist`);
158
+ passed++;
159
+ } catch (e) {
160
+ if (e.message.includes("VERIFICATION_FAILED")) {
161
+ console.log(` ❌ Element unexpectedly exists`);
162
+ } else {
163
+ console.log(` ❌ Error: ${e.message.substring(0, 60)}`);
164
+ }
165
+ failed++;
166
+ }
167
+
168
+ // Test 4: Scoped locator search
169
+ console.log(` Test 4: Scoped locator within app window`);
170
+ try {
171
+ const element = await appElement.locator(config.existsSelector).first(3000);
172
+ if (element) {
173
+ console.log(` ✅ Scoped locator found element: role=${element.role()}`);
174
+ passed++;
175
+ }
176
+ } catch (e) {
177
+ console.log(` ❌ Scoped locator failed: ${e.message.substring(0, 60)}`);
178
+ failed++;
179
+ }
180
+
181
+ } finally {
182
+ // Cleanup
183
+ if (appElement) {
184
+ console.log(` Closing ${config.name}...`);
185
+ await closeApp(appElement, config.name);
186
+ }
187
+ }
188
+
189
+ return { passed, failed, name: config.name };
190
+ }
191
+
192
+ async function runTests() {
193
+ console.log("═══════════════════════════════════════════════════════════════════");
194
+ console.log("Cross-Application Verification Test Suite");
195
+ console.log("Tests SDK verification methods across Windows built-in applications");
196
+ console.log("═══════════════════════════════════════════════════════════════════");
197
+
198
+ let totalPassed = 0;
199
+ let totalFailed = 0;
200
+ const results = [];
201
+
202
+ for (const config of APP_TESTS) {
203
+ try {
204
+ const result = await testApp(config);
205
+ totalPassed += result.passed;
206
+ totalFailed += result.failed;
207
+ results.push(result);
208
+ } catch (error) {
209
+ console.log(`\n ❌ ${config.name} test suite crashed: ${error.message}`);
210
+ results.push({ name: config.name, passed: 0, failed: 1, error: error.message });
211
+ totalFailed++;
212
+ }
213
+
214
+ // Brief pause between apps
215
+ await new Promise(r => setTimeout(r, 1000));
216
+ }
217
+
218
+ console.log("\n═══════════════════════════════════════════════════════════════════");
219
+ console.log("Results Summary:");
220
+ console.log("═══════════════════════════════════════════════════════════════════");
221
+
222
+ let totalSkipped = 0;
223
+ for (const result of results) {
224
+ if (result.skipped) {
225
+ console.log(` ⏭️ ${result.name}: SKIPPED (not installed)`);
226
+ totalSkipped++;
227
+ } else {
228
+ const status = result.failed === 0 ? "✅" : "❌";
229
+ console.log(` ${status} ${result.name}: ${result.passed} passed, ${result.failed} failed`);
230
+ }
231
+ }
232
+
233
+ console.log("───────────────────────────────────────────────────────────────────");
234
+ console.log(` TOTAL: ${totalPassed} passed, ${totalFailed} failed, ${totalSkipped} skipped`);
235
+ console.log("═══════════════════════════════════════════════════════════════════");
236
+
237
+ process.exit(totalFailed > 0 ? 1 : 0);
238
+ }
239
+
240
+ runTests().catch(error => {
241
+ console.error("Test suite crashed:", error);
242
+ process.exit(1);
243
+ });
@@ -0,0 +1,169 @@
1
+ const { Desktop } = require("../index.js");
2
+
3
+ /**
4
+ * Test for Desktop.verifyElementExists() and Desktop.verifyElementNotExists() methods
5
+ * These tests verify the verification methods work correctly with window-scoped searches
6
+ */
7
+
8
+ async function testVerifyElementExists() {
9
+ console.log("🔍 Testing Desktop.verifyElementExists()...");
10
+ const desktop = new Desktop();
11
+
12
+ // Open Notepad for testing
13
+ console.log(" Opening Notepad...");
14
+ const notepad = await desktop.openApplication("notepad.exe");
15
+ await new Promise(r => setTimeout(r, 1000));
16
+
17
+ try {
18
+ // Test 1: Find an element that exists (Document/text editor)
19
+ console.log(" Test 1: Verify existing element (Document)");
20
+ const textEditor = await desktop.verifyElementExists(
21
+ notepad,
22
+ "role:Document || role:Edit",
23
+ 5000
24
+ );
25
+
26
+ if (!textEditor) {
27
+ throw new Error("Expected to find text editor element");
28
+ }
29
+ console.log(` ✅ Found: role=${textEditor.role()}, name=${textEditor.name()}`);
30
+
31
+ // Test 2: Find MenuBar (tests menubar role mapping)
32
+ console.log(" Test 2: Verify MenuBar exists");
33
+ const menuBar = await desktop.verifyElementExists(
34
+ notepad,
35
+ "role:MenuBar",
36
+ 5000
37
+ );
38
+
39
+ if (!menuBar) {
40
+ throw new Error("Expected to find MenuBar element");
41
+ }
42
+ console.log(` ✅ Found: role=${menuBar.role()}`);
43
+
44
+ // Test 3: Non-existent element should throw
45
+ console.log(" Test 3: Verify non-existent element throws error");
46
+ let threwError = false;
47
+ try {
48
+ await desktop.verifyElementExists(
49
+ notepad,
50
+ "role:Button && name:NonExistentButton12345",
51
+ 2000
52
+ );
53
+ } catch (e) {
54
+ threwError = true;
55
+ console.log(` ✅ Correctly threw error for non-existent element`);
56
+ }
57
+
58
+ if (!threwError) {
59
+ throw new Error("Expected verifyElementExists to throw for non-existent element");
60
+ }
61
+
62
+ return true;
63
+ } finally {
64
+ // Cleanup
65
+ console.log(" Closing Notepad...");
66
+ try {
67
+ await notepad.pressKey("Alt+F4");
68
+ await new Promise(r => setTimeout(r, 500));
69
+ const dontSave = await notepad.locator("role:Button && name:Don\\'t Save").first();
70
+ await dontSave.click();
71
+ } catch {
72
+ // No save dialog
73
+ }
74
+ }
75
+ }
76
+
77
+ async function testVerifyElementNotExists() {
78
+ console.log("\n🔍 Testing Desktop.verifyElementNotExists()...");
79
+ const desktop = new Desktop();
80
+
81
+ // Open Notepad for testing
82
+ console.log(" Opening Notepad...");
83
+ const notepad = await desktop.openApplication("notepad.exe");
84
+ await new Promise(r => setTimeout(r, 1000));
85
+
86
+ try {
87
+ // Test 1: Verify non-existent element passes
88
+ console.log(" Test 1: Verify non-existent element (Save As dialog)");
89
+ await desktop.verifyElementNotExists(
90
+ notepad,
91
+ "role:Window && name:Save As",
92
+ 2000
93
+ );
94
+ console.log(" ✅ Correctly confirmed 'Save As' dialog does not exist");
95
+
96
+ // Test 2: Verify existing element throws
97
+ console.log(" Test 2: Verify existing element (MenuBar) throws error");
98
+ let threwError = false;
99
+ try {
100
+ await desktop.verifyElementNotExists(
101
+ notepad,
102
+ "role:MenuBar",
103
+ 2000
104
+ );
105
+ } catch (e) {
106
+ threwError = true;
107
+ if (e.message.includes("VERIFICATION_FAILED")) {
108
+ console.log(" ✅ Correctly threw VERIFICATION_FAILED for existing element");
109
+ } else {
110
+ throw new Error(`Expected VERIFICATION_FAILED error, got: ${e.message}`);
111
+ }
112
+ }
113
+
114
+ if (!threwError) {
115
+ throw new Error("Expected verifyElementNotExists to throw for existing element");
116
+ }
117
+
118
+ return true;
119
+ } finally {
120
+ // Cleanup
121
+ console.log(" Closing Notepad...");
122
+ try {
123
+ await notepad.pressKey("Alt+F4");
124
+ await new Promise(r => setTimeout(r, 500));
125
+ const dontSave = await notepad.locator("role:Button && name:Don\\'t Save").first();
126
+ await dontSave.click();
127
+ } catch {
128
+ // No save dialog
129
+ }
130
+ }
131
+ }
132
+
133
+ async function runTests() {
134
+ console.log("═══════════════════════════════════════════════════════════════");
135
+ console.log("Desktop Verification Methods Test Suite");
136
+ console.log("═══════════════════════════════════════════════════════════════\n");
137
+
138
+ let passed = 0;
139
+ let failed = 0;
140
+
141
+ try {
142
+ await testVerifyElementExists();
143
+ passed++;
144
+ console.log("\n✅ testVerifyElementExists PASSED\n");
145
+ } catch (error) {
146
+ failed++;
147
+ console.error("\n❌ testVerifyElementExists FAILED:", error.message, "\n");
148
+ }
149
+
150
+ try {
151
+ await testVerifyElementNotExists();
152
+ passed++;
153
+ console.log("\n✅ testVerifyElementNotExists PASSED\n");
154
+ } catch (error) {
155
+ failed++;
156
+ console.error("\n❌ testVerifyElementNotExists FAILED:", error.message, "\n");
157
+ }
158
+
159
+ console.log("═══════════════════════════════════════════════════════════════");
160
+ console.log(`Results: ${passed} passed, ${failed} failed`);
161
+ console.log("═══════════════════════════════════════════════════════════════");
162
+
163
+ process.exit(failed > 0 ? 1 : 0);
164
+ }
165
+
166
+ runTests().catch(error => {
167
+ console.error("Test suite crashed:", error);
168
+ process.exit(1);
169
+ });
@@ -0,0 +1,158 @@
1
+ const { Desktop } = require("../index.js");
2
+
3
+ /**
4
+ * Test for Element.locator() chaining functionality
5
+ * This test verifies the fix for issue #258 where Element.locator() chaining would fail
6
+ * due to expensive WindowsEngine creation on every call.
7
+ */
8
+ async function testElementChaining() {
9
+ console.log("🔗 Testing Element.locator() chaining...");
10
+
11
+ try {
12
+ const desktop = new Desktop();
13
+
14
+ // Get any available application for testing
15
+ const apps = desktop.applications();
16
+ if (apps.length === 0) {
17
+ throw new Error("No applications found for testing");
18
+ }
19
+
20
+ const testApp = apps[0];
21
+ console.log(`📱 Testing with app: ${testApp.name()}`);
22
+
23
+ // Test 1: Basic chaining - this was the main issue
24
+ console.log("Test 1: Basic element chaining");
25
+ const windowLocator = testApp.locator("role:window");
26
+ const buttonLocator = windowLocator.locator("role:button");
27
+ console.log("✅ Basic chaining works");
28
+
29
+ // Test 2: Multiple chaining levels
30
+ console.log("Test 2: Multiple chaining levels");
31
+ const deepChain = testApp
32
+ .locator("role:window")
33
+ .locator("role:pane")
34
+ .locator("role:button");
35
+ console.log("✅ Deep chaining works");
36
+
37
+ // Test 3: Stress test - create many locators quickly
38
+ console.log("Test 3: Stress test (50 rapid chains)");
39
+ const startTime = Date.now();
40
+ for (let i = 0; i < 50; i++) {
41
+ const locator = testApp.locator("role:window").locator("role:button");
42
+ // Just create the locator, don't need to use it
43
+ }
44
+ const endTime = Date.now();
45
+ console.log(`✅ Created 50 chained locators in ${endTime - startTime}ms`);
46
+
47
+ // Test 4: Concurrent chaining
48
+ console.log("Test 4: Concurrent chaining");
49
+ const promises = [];
50
+ for (let i = 0; i < 10; i++) {
51
+ promises.push(
52
+ Promise.resolve().then(() => {
53
+ return testApp.locator("role:window").locator("role:text");
54
+ })
55
+ );
56
+ }
57
+ await Promise.all(promises);
58
+ console.log("✅ Concurrent chaining works");
59
+
60
+ return true;
61
+ } catch (error) {
62
+ console.error("❌ Element chaining test failed:", error.message);
63
+ return false;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Test for Wikipedia-specific chaining (if Wikipedia is available)
69
+ */
70
+ async function testWikipediaChaining() {
71
+ console.log("🌐 Testing Wikipedia-specific chaining...");
72
+
73
+ try {
74
+ const desktop = new Desktop();
75
+
76
+ // Try to find Wikipedia page
77
+ try {
78
+ const wikipediaLocator = desktop.locator("role:document|wikipedia");
79
+ const w = await wikipediaLocator.first(0); // timeout in ms (0 = immediate)
80
+ console.log(`📖 Found Wikipedia page: ${w.name()}`);
81
+
82
+ // Test the original failing case
83
+ const textLocator = w.locator("role:text|wikipedia");
84
+ console.log("✅ Wikipedia element chaining works");
85
+
86
+ // Try to find some elements
87
+ try {
88
+ const elements = await textLocator.all(2000);
89
+ console.log(`✅ Found ${elements.length} Wikipedia text elements`);
90
+ } catch (err) {
91
+ console.log("ℹ️ No specific Wikipedia text elements found (normal)");
92
+ }
93
+
94
+ return true;
95
+ } catch (err) {
96
+ if (err.message.includes("Timed out")) {
97
+ console.log(
98
+ "ℹ️ No Wikipedia page found - skipping Wikipedia-specific test"
99
+ );
100
+ return true; // Not a failure, just no Wikipedia available
101
+ }
102
+ throw err;
103
+ }
104
+ } catch (error) {
105
+ console.error("❌ Wikipedia chaining test failed:", error.message);
106
+ return false;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Main test runner
112
+ */
113
+ async function runChainingTests() {
114
+ console.log("🚀 Starting Element.locator() chaining tests...\n");
115
+
116
+ let passed = 0;
117
+ let total = 0;
118
+
119
+ // Test 1: Basic element chaining
120
+ total++;
121
+ if (await testElementChaining()) {
122
+ passed++;
123
+ }
124
+
125
+ console.log(); // Empty line
126
+
127
+ // Test 2: Wikipedia-specific chaining
128
+ total++;
129
+ if (await testWikipediaChaining()) {
130
+ passed++;
131
+ }
132
+
133
+ console.log(); // Empty line
134
+
135
+ // Results
136
+ if (passed === total) {
137
+ console.log(`🎉 All chaining tests passed! (${passed}/${total})`);
138
+ process.exit(0);
139
+ } else {
140
+ console.log(`❌ Some tests failed: ${passed}/${total} passed`);
141
+ process.exit(1);
142
+ }
143
+ }
144
+
145
+ // Export for use in other test files
146
+ module.exports = {
147
+ testElementChaining,
148
+ testWikipediaChaining,
149
+ runChainingTests,
150
+ };
151
+
152
+ // Run tests if this file is executed directly
153
+ if (require.main === module) {
154
+ runChainingTests().catch((error) => {
155
+ console.error("💥 Test runner crashed:", error);
156
+ process.exit(1);
157
+ });
158
+ }