testdriverai 4.1.26 → 4.1.28

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.
package/agent.js CHANGED
@@ -215,11 +215,21 @@ const exit = async (failed = true) => {
215
215
  });
216
216
  };
217
217
 
218
+ const dieOnFatal = async (error) => {
219
+ console.log("");
220
+ log.log("info", chalk.red("Fatal Error") + `\n${error.message}`);
221
+ await summarize(error.message);
222
+ return await exit(true);
223
+ }
224
+
218
225
  // creates a new "thread" in which the AI is given an error
219
226
  // and responds. notice `actOnMarkdown` which will continue
220
227
  // the thread until there are no more codeblocks to execute
221
228
  const haveAIResolveError = async (error, markdown, depth = 0, undo = true) => {
222
-
229
+
230
+ if (thisCommand == "run" || error.fatal) {
231
+ return await dieOnFatal(error);
232
+ }
223
233
 
224
234
  let eMessage = error.message ? error.message : error;
225
235
 
@@ -350,19 +360,12 @@ const runCommand = async (command, depth) => {
350
360
  return await actOnMarkdown(response, depth);
351
361
  }
352
362
  } catch (error) {
353
- if (thisCommand == "run") {
354
- console.log("");
355
- log.log("info", chalk.red("Fatal Error") + `\n${error.message}`);
356
- await summarize(error.message);
357
- return await exit(true);
358
- } else {
359
- return await haveAIResolveError(
360
- error,
361
- yaml.dump({ commands: [yml] }),
362
- depth,
363
- true,
364
- );
365
- }
363
+ return await haveAIResolveError(
364
+ error,
365
+ yaml.dump({ commands: [yml] }),
366
+ depth,
367
+ true,
368
+ );
366
369
  }
367
370
  };
368
371
 
package/lib/commander.js CHANGED
@@ -12,7 +12,6 @@ const sdk = require("./sdk");
12
12
  // object is a json representation of the individual yml command
13
13
  // the process turns markdown -> yml -> json -> js function execution
14
14
  const run = async (object, depth) => {
15
-
16
15
  log("debug", { object, depth });
17
16
 
18
17
  // success returns null
@@ -63,7 +62,11 @@ commands:
63
62
  speak(`scrolling ${object.direction}`);
64
63
  log("info", generator.jsonToManual(object));
65
64
  notify(generator.jsonToManual(object, false));
66
- response = await commands.scroll(object.direction, object.amount);
65
+ response = await commands.scroll(
66
+ object.direction,
67
+ object.amount,
68
+ object.method,
69
+ );
67
70
  break;
68
71
  case "wait":
69
72
  speak(`waiting ${object.timeout} seconds`);
@@ -143,6 +146,7 @@ commands:
143
146
  object.text,
144
147
  object.direction,
145
148
  object.distance,
149
+ object.textMatchMethod,
146
150
  object.method,
147
151
  );
148
152
  break;
@@ -154,6 +158,7 @@ commands:
154
158
  object.description,
155
159
  object.direction,
156
160
  object.distance,
161
+ object.method,
157
162
  );
158
163
  break;
159
164
  case "focus-application":
@@ -179,10 +184,10 @@ commands:
179
184
  }
180
185
 
181
186
  let timing = marky.stop(object.command);
182
-
187
+
183
188
  await Promise.all([
184
- sdk.req('ran', { command: object.command, data: object }),
185
- analytics.track("command", { data: object, depth, timing })
189
+ sdk.req("ran", { command: object.command, data: object }),
190
+ analytics.track("command", { data: object, depth, timing }),
186
191
  ]);
187
192
 
188
193
  return response;
package/lib/commands.js CHANGED
@@ -129,7 +129,7 @@ const assert = async (assertion, shouldThrow = false, async = false) => {
129
129
  return true;
130
130
  } else {
131
131
  if (shouldThrow) {
132
- throw new AiError(`AI Assertion failed`);
132
+ throw new AiError(`AI Assertion failed`, true);
133
133
  } else {
134
134
  return false;
135
135
  }
@@ -160,18 +160,33 @@ const assert = async (assertion, shouldThrow = false, async = false) => {
160
160
  return handleAssertResponse(response.data);
161
161
  }
162
162
  };
163
- const scroll = async (direction = "down", amount = 300) => {
163
+ const scroll = async (direction = "down", amount = 300, method = "keyboard") => {
164
164
  await redraw.start();
165
165
 
166
166
  amount = parseInt(amount);
167
167
 
168
+ if (method === "mouse") {
169
+ // after experimenting, 200 is a good default for mouse, mostly as mouse will be called only when the user asks for it
170
+ // and that happens when keyboard scrolling cannot do things when there's a pop up that needs to be scrolled over and
171
+ // pop ups are usually smaller and needs a smaller amount of scrolling
172
+ amount = 200;
173
+ }
174
+
168
175
  switch (direction) {
169
176
  case "up":
170
- await robot.keyTap("pageup");
177
+ if (method === "mouse") {
178
+ await robot.scrollMouse(0, amount );
179
+ } else {
180
+ await robot.keyTap("pageup");
181
+ }
171
182
  await redraw.wait(2500);
172
183
  break;
173
184
  case "down":
174
- await robot.keyTap("pagedown");
185
+ if (method === "mouse") {
186
+ await robot.scrollMouse(0, amount * -1);
187
+ } else {
188
+ await robot.keyTap("pagedown");
189
+ }
175
190
  await redraw.wait(2500);
176
191
  break;
177
192
  case "left":
@@ -321,7 +336,7 @@ let commands = {
321
336
  mdStream.end();
322
337
 
323
338
  if (!response?.data) {
324
- throw new AiError("No text on screen matches description", true);
339
+ throw new AiError("No image or icon on screen matches description");
325
340
  } else {
326
341
  return response.data;
327
342
  }
@@ -335,7 +350,7 @@ let commands = {
335
350
  let result = await findImageOnScreen(relativePath, image);
336
351
 
337
352
  if (!result) {
338
- throw new AiError(`Image not found: ${relativePath}`);
353
+ throw new AiError(`Image not found: ${relativePath}`, true);
339
354
  } else {
340
355
  if (action === "click") {
341
356
  await click(result.centerX, result.centerY, action);
@@ -521,29 +536,38 @@ let commands = {
521
536
  text,
522
537
  direction = "down",
523
538
  maxDistance = 1200,
524
- method = "turbo",
539
+ textMatchMethod = "turbo",
540
+ method = "keyboard"
525
541
  ) => {
526
542
  await redraw.start();
527
543
 
528
544
  log("info", chalk.dim(`scrolling for text: "${text}"...`), true);
529
545
 
530
- try {
531
- // use robot to press CMD+F
532
- await robot.keyTap("f", commandOrControl);
533
- // type the text
534
- await robot.typeString(text);
535
- await redraw.wait(5000);
536
- await robot.keyTap("escape");
537
- } catch (e) {
538
- console.log(e);
539
- throw new AiError(
540
- "Could not find element using browser text search",
541
- true,
542
- );
546
+ if (method === "keyboard") {
547
+ try {
548
+ // use robot to press CMD+F
549
+ await robot.keyTap("f", commandOrControl);
550
+ // keyTap will release the normal keys, but will not release modifier keys
551
+ // so we need to release the modifier keys manually
552
+ robot.keyToggle(commandOrControl, "up");
553
+ // type the text
554
+ await robot.typeString(text);
555
+ await redraw.wait(5000);
556
+ await robot.keyTap("escape");
557
+ } catch (e) {
558
+ console.log(e);
559
+ throw new AiError(
560
+ "Could not find element using browser text search",
561
+ true,
562
+ );
563
+ }
543
564
  }
544
565
 
545
566
  let scrollDistance = 0;
546
567
  let incrementDistance = 300;
568
+ if (method === "mouse") {
569
+ incrementDistance = 200;
570
+ }
547
571
  let passed = false;
548
572
 
549
573
  while (scrollDistance <= maxDistance && !passed) {
@@ -551,7 +575,7 @@ let commands = {
551
575
  "assert/text",
552
576
  {
553
577
  needle: text,
554
- method: method,
578
+ method: textMatchMethod,
555
579
  image: await captureScreenBase64(),
556
580
  },
557
581
  (chunk) => {
@@ -570,7 +594,7 @@ let commands = {
570
594
  ),
571
595
  true,
572
596
  );
573
- await scroll(direction);
597
+ await scroll(direction, incrementDistance, method);
574
598
  scrollDistance = scrollDistance + incrementDistance;
575
599
  }
576
600
  }
@@ -589,6 +613,7 @@ let commands = {
589
613
  description,
590
614
  direction = "down",
591
615
  maxDistance = 10000,
616
+ method = "keyboard",
592
617
  ) => {
593
618
  log(
594
619
  "info",
@@ -613,7 +638,7 @@ let commands = {
613
638
  chalk.dim(`scrolling ${direction} ${incrementDistance} pixels...`),
614
639
  true,
615
640
  );
616
- await scroll(direction);
641
+ await scroll(direction, incrementDistance, method);
617
642
  scrollDistance = scrollDistance + incrementDistance;
618
643
  }
619
644
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "testdriverai",
3
- "version": "4.1.26",
3
+ "version": "4.1.28",
4
4
  "description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
5
5
  "main": "index.js",
6
6
  "bin": {