testdriverai 4.2.15 → 4.2.16
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/.github/workflows/test_interp.yml +27 -0
- package/agent.js +51 -17
- package/package.json +4 -3
- package/test.yml +18 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
name: Test Interp
|
|
3
|
+
|
|
4
|
+
on:
|
|
5
|
+
push:
|
|
6
|
+
branches: ["main"]
|
|
7
|
+
pull_request:
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
run:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: testdriverai/action@main
|
|
15
|
+
with:
|
|
16
|
+
os: windows
|
|
17
|
+
branch: main
|
|
18
|
+
key: ${{secrets.TESTDRIVER_API_KEY}}
|
|
19
|
+
prompt: |
|
|
20
|
+
1. /run test.yml
|
|
21
|
+
prerun: |
|
|
22
|
+
Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "${{ env.WEBSITE_URL }}"
|
|
23
|
+
|
|
24
|
+
env:
|
|
25
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
26
|
+
WEBSITE_URL: "https://kzmgtp9k2gtuuw2iquk2.lite.vusercontent.net/"
|
|
27
|
+
TD_PASSWORD: "passW0rd!"
|
package/agent.js
CHANGED
|
@@ -205,7 +205,6 @@ if (!commandHistory.length) {
|
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
const exit = async (failed = true, shouldSave = false) => {
|
|
208
|
-
|
|
209
208
|
if (shouldSave) {
|
|
210
209
|
await save();
|
|
211
210
|
}
|
|
@@ -241,7 +240,7 @@ const haveAIResolveError = async (error, markdown, depth = 0, undo = true) => {
|
|
|
241
240
|
|
|
242
241
|
logger.error(eMessage);
|
|
243
242
|
|
|
244
|
-
logger.debug("%j",
|
|
243
|
+
logger.debug("%j", error);
|
|
245
244
|
logger.debug("%s", error.stack);
|
|
246
245
|
|
|
247
246
|
log.prettyMarkdown(eMessage);
|
|
@@ -377,7 +376,6 @@ let csv = [["command,time"]];
|
|
|
377
376
|
|
|
378
377
|
const executeCommands = async (commands, depth, pushToHistory = false) => {
|
|
379
378
|
if (commands?.length) {
|
|
380
|
-
|
|
381
379
|
for (const command of commands) {
|
|
382
380
|
if (pushToHistory) {
|
|
383
381
|
executionHistory[executionHistory.length - 1]?.commands.push(command);
|
|
@@ -454,7 +452,6 @@ const aiExecute = async (message, validateAndLoop = false) => {
|
|
|
454
452
|
};
|
|
455
453
|
|
|
456
454
|
const loadYML = async (file) => {
|
|
457
|
-
|
|
458
455
|
let yml;
|
|
459
456
|
|
|
460
457
|
//wrap this in try/catch so if the file doesn't exist output an error message to the user
|
|
@@ -469,7 +466,9 @@ const loadYML = async (file) => {
|
|
|
469
466
|
await exit(true);
|
|
470
467
|
}
|
|
471
468
|
|
|
472
|
-
let interpolationVars = JSON.parse(
|
|
469
|
+
let interpolationVars = JSON.parse(
|
|
470
|
+
process.env["TD_INTERPOLATION_VARS"] || "{}",
|
|
471
|
+
);
|
|
473
472
|
|
|
474
473
|
// Inject environment variables into any ${VAR} strings
|
|
475
474
|
yml = parser.interpolate(yml, process.env);
|
|
@@ -477,7 +476,6 @@ const loadYML = async (file) => {
|
|
|
477
476
|
// Inject any vars from the TD_INTERPOLATION_VARS variable (typically from the action)
|
|
478
477
|
yml = parser.interpolate(yml, interpolationVars);
|
|
479
478
|
|
|
480
|
-
|
|
481
479
|
let ymlObj = null;
|
|
482
480
|
try {
|
|
483
481
|
ymlObj = await yaml.load(yml);
|
|
@@ -490,8 +488,7 @@ const loadYML = async (file) => {
|
|
|
490
488
|
}
|
|
491
489
|
|
|
492
490
|
return ymlObj;
|
|
493
|
-
|
|
494
|
-
}
|
|
491
|
+
};
|
|
495
492
|
|
|
496
493
|
const assert = async (expect) => {
|
|
497
494
|
analytics.track("assert");
|
|
@@ -564,7 +561,7 @@ const humanInput = async (currentTask, validateAndLoop = false) => {
|
|
|
564
561
|
await save({ silent: true });
|
|
565
562
|
};
|
|
566
563
|
|
|
567
|
-
const generate = async (type, count) => {
|
|
564
|
+
const generate = async (type, count, baseYaml, skipYaml = false) => {
|
|
568
565
|
logger.debug("generate called, %s", type);
|
|
569
566
|
|
|
570
567
|
speak("thinking...");
|
|
@@ -573,6 +570,10 @@ const generate = async (type, count) => {
|
|
|
573
570
|
logger.info(chalk.dim("thinking..."), true);
|
|
574
571
|
logger.info("");
|
|
575
572
|
|
|
573
|
+
if (baseYaml && !skipYaml) {
|
|
574
|
+
await run(baseYaml, false, false);
|
|
575
|
+
}
|
|
576
|
+
|
|
576
577
|
let image = await system.captureScreenBase64();
|
|
577
578
|
const mdStream = log.createMarkdownStreamLogger();
|
|
578
579
|
let message = await sdk.req(
|
|
@@ -613,6 +614,9 @@ const generate = async (type, count) => {
|
|
|
613
614
|
|
|
614
615
|
let list = testPrompt.listsOrdered[0];
|
|
615
616
|
|
|
617
|
+
if (baseYaml && fs.existsSync(baseYaml)) {
|
|
618
|
+
list.unshift(`/run ${baseYaml} --embed`);
|
|
619
|
+
}
|
|
616
620
|
let contents = list
|
|
617
621
|
.map((item, index) => `${index + 1}. ${item}`)
|
|
618
622
|
.join("\n");
|
|
@@ -682,7 +686,6 @@ const actOnMarkdown = async (content, depth, pushToHistory = false) => {
|
|
|
682
686
|
};
|
|
683
687
|
|
|
684
688
|
const newSession = async () => {
|
|
685
|
-
|
|
686
689
|
// should be start of new session
|
|
687
690
|
const sessionRes = await sdk.req("session/start", {
|
|
688
691
|
systemInformationOsInfo: await system.getSystemInformationOsInfo(),
|
|
@@ -691,13 +694,11 @@ const newSession = async () => {
|
|
|
691
694
|
});
|
|
692
695
|
|
|
693
696
|
session.set(sessionRes.data.id);
|
|
694
|
-
|
|
695
697
|
};
|
|
696
698
|
|
|
697
699
|
// simple function to backfill the chat history with a prompt and
|
|
698
700
|
// then call `promptUser()` to get the user input
|
|
699
701
|
const firstPrompt = async () => {
|
|
700
|
-
|
|
701
702
|
await newSession();
|
|
702
703
|
|
|
703
704
|
// readline is what allows us to get user input
|
|
@@ -734,7 +735,10 @@ const firstPrompt = async () => {
|
|
|
734
735
|
|
|
735
736
|
logger.info(""); // adds a nice break between submissions
|
|
736
737
|
|
|
737
|
-
let commands = input
|
|
738
|
+
let commands = input
|
|
739
|
+
.split(" ")
|
|
740
|
+
.map((l) => l.trim())
|
|
741
|
+
.filter((l) => l.length);
|
|
738
742
|
|
|
739
743
|
// if last character is a question mark, we assume the user is asking a question
|
|
740
744
|
if (input.indexOf("/summarize") == 0) {
|
|
@@ -750,9 +754,41 @@ const firstPrompt = async () => {
|
|
|
750
754
|
} else if (input.indexOf("/manual") == 0) {
|
|
751
755
|
await manualInput(commands.slice(1).join(" "));
|
|
752
756
|
} else if (input.indexOf("/run") == 0) {
|
|
753
|
-
|
|
757
|
+
const file = commands[1];
|
|
758
|
+
const flags = commands.slice(2);
|
|
759
|
+
let shouldSave = flags.includes("--save") ? true : false;
|
|
760
|
+
let shouldExit = flags.includes("--exit") ? true : false;
|
|
761
|
+
let shouldEmbed = flags.includes("--embed") ? true : false;
|
|
762
|
+
|
|
763
|
+
if (shouldEmbed && (shouldSave || shouldExit)) {
|
|
764
|
+
await dieOnFatal({
|
|
765
|
+
message:
|
|
766
|
+
"Cannot embed AND save or exit. Please either use --embed or use any combination of --save and --exit.",
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if (shouldEmbed) {
|
|
771
|
+
const relativePath = path.relative(
|
|
772
|
+
process.cwd(),
|
|
773
|
+
path.resolve(process.cwd(), file),
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
executionHistory.push({
|
|
777
|
+
prompt: `/run ${relativePath}`,
|
|
778
|
+
commands: [
|
|
779
|
+
{
|
|
780
|
+
command: "run",
|
|
781
|
+
file: relativePath,
|
|
782
|
+
},
|
|
783
|
+
],
|
|
784
|
+
});
|
|
785
|
+
await embed(file, 0);
|
|
786
|
+
} else {
|
|
787
|
+
await run(file, shouldSave, shouldExit);
|
|
788
|
+
}
|
|
754
789
|
} else if (input.indexOf("/generate") == 0) {
|
|
755
|
-
|
|
790
|
+
const skipYaml = commands[4] === "--skip-yaml";
|
|
791
|
+
await generate(commands[1], commands[2], commands[3], skipYaml);
|
|
756
792
|
} else {
|
|
757
793
|
await humanInput(input, true);
|
|
758
794
|
}
|
|
@@ -884,7 +920,6 @@ let summarize = async (error = null) => {
|
|
|
884
920
|
|
|
885
921
|
// this function is responsible for saving the regression test script to a file
|
|
886
922
|
let save = async ({ filepath = thisFile, silent = false } = {}) => {
|
|
887
|
-
|
|
888
923
|
analytics.track("save", { silent });
|
|
889
924
|
|
|
890
925
|
if (!silent) {
|
|
@@ -925,7 +960,6 @@ ${regression}
|
|
|
925
960
|
// it parses the markdown file and executes the codeblocks exactly as if they were
|
|
926
961
|
// generated by the AI in a single prompt
|
|
927
962
|
let run = async (file, shouldSave = false, shouldExit = true) => {
|
|
928
|
-
|
|
929
963
|
await newSession();
|
|
930
964
|
|
|
931
965
|
setTerminalWindowTransparency(true);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "testdriverai",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.16",
|
|
4
4
|
"description": "Next generation autonomous AI agent for end-to-end testing of web & desktop",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"odiff-bin": "^3.1.2",
|
|
39
39
|
"prompts": "^2.4.2",
|
|
40
40
|
"remark-parse": "^11.0.0",
|
|
41
|
-
"
|
|
41
|
+
"robotjs": "npm:@hurdlegroup/robotjs",
|
|
42
42
|
"sanitize-filename": "^1.6.3",
|
|
43
43
|
"say": "^0.16.0",
|
|
44
44
|
"screenshot-desktop": "^1.15.0",
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
},
|
|
52
52
|
"overrides": {
|
|
53
53
|
"glob": "^11.0.1",
|
|
54
|
-
"rimraf": "^5.0.10"
|
|
54
|
+
"rimraf": "^5.0.10",
|
|
55
|
+
"robotjs": "npm:@hurdlegroup/robotjs"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@eslint/js": "^9.10.0",
|
package/test.yml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
version: 4.1.40
|
|
2
|
+
steps:
|
|
3
|
+
- prompt: Enter Password n stuff
|
|
4
|
+
commands:
|
|
5
|
+
- command: focus-application
|
|
6
|
+
name: Google Chrome
|
|
7
|
+
- command: hover-text
|
|
8
|
+
text: Username
|
|
9
|
+
description: username field
|
|
10
|
+
action: click
|
|
11
|
+
- command: type
|
|
12
|
+
text: ${TD_PASSWORD}
|
|
13
|
+
- command: hover-text
|
|
14
|
+
text: Password
|
|
15
|
+
description: Password field
|
|
16
|
+
action: click
|
|
17
|
+
- command: type
|
|
18
|
+
text: ${TD_PASSWORD}
|