explorbot 0.1.6 → 0.1.8
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/bin/explorbot-cli.ts +12 -3
- package/dist/bin/explorbot-cli.js +12 -3
- package/dist/package.json +145 -0
- package/dist/src/ai/rules.js +9 -1
- package/dist/src/ai/tester.js +22 -1
- package/dist/src/utils/aria.js +1 -1
- package/package.json +1 -1
- package/src/ai/rules.ts +9 -1
- package/src/ai/tester.ts +24 -2
- package/src/utils/aria.ts +1 -1
package/bin/explorbot-cli.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import chalk from 'chalk';
|
|
5
6
|
import { Command } from 'commander';
|
|
6
7
|
import figureSet from 'figures';
|
|
@@ -20,7 +21,13 @@ import { parseMarkdownToTerminal } from '../src/utils/markdown-terminal.js';
|
|
|
20
21
|
const program = new Command();
|
|
21
22
|
const cli = getCliName();
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
const pkgPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../package.json');
|
|
25
|
+
const pkgVersion = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version as string;
|
|
26
|
+
|
|
27
|
+
program.name(cli).description('AI-powered web exploration tool').version(pkgVersion, '-V, --version');
|
|
28
|
+
program.hook('preAction', () => {
|
|
29
|
+
console.log(chalk.dim(`${cli} v${pkgVersion}`));
|
|
30
|
+
});
|
|
24
31
|
|
|
25
32
|
interface CLIOptions {
|
|
26
33
|
verbose?: boolean;
|
|
@@ -113,7 +120,7 @@ addCommonOptions(program.command('start [path]').description('Start web explorat
|
|
|
113
120
|
await startTUI(explorBot);
|
|
114
121
|
});
|
|
115
122
|
|
|
116
|
-
addCommonOptions(program.command('explore <path>').description('Explore a page autonomously and run invented scenarios').option('--max-tests <count>', 'Maximum number of tests to run')).action(async (explorePath, options) => {
|
|
123
|
+
addCommonOptions(program.command('explore <path>').description('Explore a page autonomously and run invented scenarios').option('--max-tests <count>', 'Maximum number of tests to run').option('--focus <feature>', 'Focus area for exploration')).action(async (explorePath, options) => {
|
|
117
124
|
try {
|
|
118
125
|
const explorBot = new ExplorBot(buildExplorBotOptions(explorePath, options));
|
|
119
126
|
await explorBot.start();
|
|
@@ -121,7 +128,9 @@ addCommonOptions(program.command('explore <path>').description('Explore a page a
|
|
|
121
128
|
const { ExploreCommand } = await import('../src/commands/explore-command.js');
|
|
122
129
|
const cmd = new ExploreCommand(explorBot);
|
|
123
130
|
if (options.maxTests) cmd.maxTests = Number.parseInt(options.maxTests, 10);
|
|
124
|
-
|
|
131
|
+
const execArgs: string[] = [];
|
|
132
|
+
if (options.focus) execArgs.push('--focus', `"${options.focus}"`);
|
|
133
|
+
await cmd.execute(execArgs.join(' '));
|
|
125
134
|
await explorBot.stop();
|
|
126
135
|
await showStatsAndExit(0);
|
|
127
136
|
} catch (error) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import fs from 'node:fs';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import chalk from 'chalk';
|
|
5
6
|
import { Command } from 'commander';
|
|
6
7
|
import figureSet from 'figures';
|
|
@@ -18,7 +19,12 @@ import { jsonToTable } from '../src/utils/markdown-parser.js';
|
|
|
18
19
|
import { parseMarkdownToTerminal } from '../src/utils/markdown-terminal.js';
|
|
19
20
|
const program = new Command();
|
|
20
21
|
const cli = getCliName();
|
|
21
|
-
|
|
22
|
+
const pkgPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../package.json');
|
|
23
|
+
const pkgVersion = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')).version;
|
|
24
|
+
program.name(cli).description('AI-powered web exploration tool').version(pkgVersion, '-V, --version');
|
|
25
|
+
program.hook('preAction', () => {
|
|
26
|
+
console.log(chalk.dim(`${cli} v${pkgVersion}`));
|
|
27
|
+
});
|
|
22
28
|
function buildExplorBotOptions(from, options) {
|
|
23
29
|
return {
|
|
24
30
|
from,
|
|
@@ -86,7 +92,7 @@ addCommonOptions(program.command('start [path]').description('Start web explorat
|
|
|
86
92
|
await explorBot.start();
|
|
87
93
|
await startTUI(explorBot);
|
|
88
94
|
});
|
|
89
|
-
addCommonOptions(program.command('explore <path>').description('Explore a page autonomously and run invented scenarios').option('--max-tests <count>', 'Maximum number of tests to run')).action(async (explorePath, options) => {
|
|
95
|
+
addCommonOptions(program.command('explore <path>').description('Explore a page autonomously and run invented scenarios').option('--max-tests <count>', 'Maximum number of tests to run').option('--focus <feature>', 'Focus area for exploration')).action(async (explorePath, options) => {
|
|
90
96
|
try {
|
|
91
97
|
const explorBot = new ExplorBot(buildExplorBotOptions(explorePath, options));
|
|
92
98
|
await explorBot.start();
|
|
@@ -95,7 +101,10 @@ addCommonOptions(program.command('explore <path>').description('Explore a page a
|
|
|
95
101
|
const cmd = new ExploreCommand(explorBot);
|
|
96
102
|
if (options.maxTests)
|
|
97
103
|
cmd.maxTests = Number.parseInt(options.maxTests, 10);
|
|
98
|
-
|
|
104
|
+
const execArgs = [];
|
|
105
|
+
if (options.focus)
|
|
106
|
+
execArgs.push('--focus', `"${options.focus}"`);
|
|
107
|
+
await cmd.execute(execArgs.join(' '));
|
|
99
108
|
await explorBot.stop();
|
|
100
109
|
await showStatsAndExit(0);
|
|
101
110
|
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "explorbot",
|
|
3
|
+
"version": "0.1.8",
|
|
4
|
+
"description": "CLI app built with React Ink, CodeceptJS, and Playwright",
|
|
5
|
+
"license": "Elastic-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/src/index.js",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"bun": "./src/index.tsx",
|
|
11
|
+
"import": "./dist/src/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"explorbot": "./dist/bin/explorbot-cli.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist/",
|
|
19
|
+
"src/**/*.ts",
|
|
20
|
+
"src/**/*.tsx",
|
|
21
|
+
"bin/**/*.ts",
|
|
22
|
+
"boat/api-tester/src/**/*.ts",
|
|
23
|
+
"rules/",
|
|
24
|
+
"assets/sample-files/"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "bun run src/index.tsx build && bun run build:bin",
|
|
28
|
+
"build:bin": "bun build bin/explorbot-cli.ts --outdir bin --target node --external commander --format esm",
|
|
29
|
+
"build:npm": "bash scripts/build-npm.sh",
|
|
30
|
+
"prepublishOnly": "npm run build:npm",
|
|
31
|
+
"dev": "bun run src/index.tsx",
|
|
32
|
+
"start": "node dist/index.js",
|
|
33
|
+
"init": "bun run src/index.tsx init",
|
|
34
|
+
"test": "codeceptjs run",
|
|
35
|
+
"test:headless": "codeceptjs run --headless",
|
|
36
|
+
"test:ui": "bun test tests/ui",
|
|
37
|
+
"test:unit": "bun test tests/unit",
|
|
38
|
+
"test:node": "node --test tests/node/*.mjs",
|
|
39
|
+
"test:unit:coverage": "bun test tests/unit --coverage && find . -name '.lcov.info*.tmp' -type f -delete 2>/dev/null || true",
|
|
40
|
+
"test:coverage": "bun test tests/unit --coverage --coverage-reporter=text && find . -name '.lcov.info*.tmp' -type f -delete 2>/dev/null || true",
|
|
41
|
+
"test:coverage:html": "echo 'Run: bun test tests/unit --coverage for detailed coverage report'",
|
|
42
|
+
"test:coverage:summary": "bun test tests/unit --coverage --coverage-reporter=text | tail -n 20 && find . -name '.lcov.info*.tmp' -type f -delete 2>/dev/null || true",
|
|
43
|
+
"test:coverage:clean": "find . -name '.lcov.info*.tmp' -type f -delete 2>/dev/null || true",
|
|
44
|
+
"format": "biome format --write .",
|
|
45
|
+
"format:check": "biome format .",
|
|
46
|
+
"lint": "biome lint .",
|
|
47
|
+
"lint:fix": "biome lint --write .",
|
|
48
|
+
"check": "biome check .",
|
|
49
|
+
"check:fix": "biome check --write .",
|
|
50
|
+
"langfuse:export": "bun run .claude/skills/explorbot-debug/langfuse-export.ts"
|
|
51
|
+
},
|
|
52
|
+
"keywords": [
|
|
53
|
+
"cli",
|
|
54
|
+
"react",
|
|
55
|
+
"ink",
|
|
56
|
+
"codeceptjs",
|
|
57
|
+
"playwright"
|
|
58
|
+
],
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "https://github.com/testomatio/explorbot"
|
|
62
|
+
},
|
|
63
|
+
"author": "",
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"@ai-sdk/anthropic": "^3.0",
|
|
66
|
+
"@ai-sdk/groq": "^3.0",
|
|
67
|
+
"@ai-sdk/openai": "^3.0",
|
|
68
|
+
"@axe-core/playwright": "^4.11.0",
|
|
69
|
+
"@codeceptjs/reflection": "^0.5.2",
|
|
70
|
+
"@inkjs/ui": "^2.0.0",
|
|
71
|
+
"@langfuse/otel": "^4.5.1",
|
|
72
|
+
"@openrouter/ai-sdk-provider": "^2.3.3",
|
|
73
|
+
"@opentelemetry/api": "^1.9.0",
|
|
74
|
+
"@opentelemetry/auto-instrumentations-node": "^0.67.3",
|
|
75
|
+
"@opentelemetry/instrumentation": "^0.208.0",
|
|
76
|
+
"@opentelemetry/resources": "^2.2.0",
|
|
77
|
+
"@opentelemetry/sdk-node": "^0.208.0",
|
|
78
|
+
"@opentelemetry/sdk-trace-base": "^2.2.0",
|
|
79
|
+
"@opentelemetry/semantic-conventions": "^1.38.0",
|
|
80
|
+
"@scalar/openapi-parser": "^0.25.6",
|
|
81
|
+
"@testomatio/reporter": "^2.7.6",
|
|
82
|
+
"ai": "^6.0.6",
|
|
83
|
+
"axe-core": "^4.11.1",
|
|
84
|
+
"bash-tool": "^1.3.15",
|
|
85
|
+
"cli-highlight": "^2.1.11",
|
|
86
|
+
"codeceptjs": "4.0.0-rc.11",
|
|
87
|
+
"commander": "^14.0.1",
|
|
88
|
+
"debug": "^4.4.3",
|
|
89
|
+
"dedent": "^1.6.0",
|
|
90
|
+
"expect": "^30.3.0",
|
|
91
|
+
"figures": "^6.1.0",
|
|
92
|
+
"gray-matter": "^4.0.3",
|
|
93
|
+
"html-minifier-next": "^2.1.5",
|
|
94
|
+
"ink": "^6.3.1",
|
|
95
|
+
"ink-big-text": "^2.0.0",
|
|
96
|
+
"ink-select-input": "^6.2.0",
|
|
97
|
+
"ink-text-input": "^6.0.0",
|
|
98
|
+
"jsdom": "^28.0.0",
|
|
99
|
+
"jsonpath-plus": "^10.4.0",
|
|
100
|
+
"just-bash": "^2.14.0",
|
|
101
|
+
"langfuse": "^3.5.0",
|
|
102
|
+
"marked": "^16.2.0",
|
|
103
|
+
"marked-terminal": "^7.3.0",
|
|
104
|
+
"micromatch": "^4.0.8",
|
|
105
|
+
"ora-classic": "^5.4.2",
|
|
106
|
+
"parse5": "^8.0.0",
|
|
107
|
+
"playwright": "^1.40.0",
|
|
108
|
+
"react": "^19.1.1",
|
|
109
|
+
"strip-ansi": "^7.1.2",
|
|
110
|
+
"turndown": "^7.2.1",
|
|
111
|
+
"unique-names-generator": "^4.7.1",
|
|
112
|
+
"yargs": "^17.7.2",
|
|
113
|
+
"zod": "^4.1.8"
|
|
114
|
+
},
|
|
115
|
+
"devDependencies": {
|
|
116
|
+
"@biomejs/biome": "^1.5.3",
|
|
117
|
+
"@copilotkit/aimock": "^1.14.0",
|
|
118
|
+
"@testing-library/react": "^16.3.0",
|
|
119
|
+
"@types/debug": "^4.1.12",
|
|
120
|
+
"@types/jsdom": "^27.0.0",
|
|
121
|
+
"@types/micromatch": "^4.0.9",
|
|
122
|
+
"@types/react": "^18.2.0",
|
|
123
|
+
"@types/yargs": "^17.0.24",
|
|
124
|
+
"bunosh": "^0.4.0",
|
|
125
|
+
"ink-testing-library": "^4.0.0",
|
|
126
|
+
"langwatch": "^0.10.0",
|
|
127
|
+
"msw": "^2.11.3",
|
|
128
|
+
"typescript": "^5.0.0",
|
|
129
|
+
"vitest": "^3.2.4"
|
|
130
|
+
},
|
|
131
|
+
"engines": {
|
|
132
|
+
"node": ">=24.0.0"
|
|
133
|
+
},
|
|
134
|
+
"overrides": {
|
|
135
|
+
"has-flag": "4.0.0",
|
|
136
|
+
"supports-color": "7.2.0"
|
|
137
|
+
},
|
|
138
|
+
"resolutions": {
|
|
139
|
+
"marked-terminal": {
|
|
140
|
+
"supports-hyperlinks": "2.3.0"
|
|
141
|
+
},
|
|
142
|
+
"has-flag": "4.0.0",
|
|
143
|
+
"supports-color": "7.2.0"
|
|
144
|
+
}
|
|
145
|
+
}
|
package/dist/src/ai/rules.js
CHANGED
|
@@ -131,7 +131,15 @@ export const fileUploadRule = dedent `
|
|
|
131
131
|
export const protectionRule = dedent `
|
|
132
132
|
<important>
|
|
133
133
|
Do not sign out current user of the application.
|
|
134
|
-
Do not change current user account settings
|
|
134
|
+
Do not change current user account settings.
|
|
135
|
+
|
|
136
|
+
Pre-existing data on the page belongs to the application, not the test.
|
|
137
|
+
Items that were not created inside the current test scenario must not be deleted, removed, emptied, reset, archived, or otherwise destroyed.
|
|
138
|
+
If a scenario needs to verify destructive behaviour, the same scenario must first create a disposable target and then destroy that specific target — never operate on data that was already there when the test started.
|
|
139
|
+
|
|
140
|
+
The resource that the current page URL represents is "under test".
|
|
141
|
+
The test must not destroy the resource it is running against — doing so invalidates every subsequent scenario that starts on the same URL.
|
|
142
|
+
Do not propose or perform delete/remove/archive actions on the entity that owns the current URL; propose such actions only on disposable children created within the scenario itself.
|
|
135
143
|
</important>
|
|
136
144
|
`;
|
|
137
145
|
export const focusedElementRule = dedent `
|
package/dist/src/ai/tester.js
CHANGED
|
@@ -12,7 +12,7 @@ import { detectFocusArea, extractFocusedElement } from "../utils/aria.js";
|
|
|
12
12
|
import { HooksRunner } from "../utils/hooks-runner.js";
|
|
13
13
|
import { createDebug, tag } from "../utils/logger.js";
|
|
14
14
|
import { loop } from "../utils/loop.js";
|
|
15
|
-
import { actionRule, focusedElementRule, locatorRule, multipleTabsRule, sectionContextRule } from "./rules.js";
|
|
15
|
+
import { actionRule, focusedElementRule, locatorRule, multipleTabsRule, protectionRule, sectionContextRule } from "./rules.js";
|
|
16
16
|
import { TaskAgent } from "./task-agent.js";
|
|
17
17
|
import { createCodeceptJSTools, createSpecialContextTools } from "./tools.js";
|
|
18
18
|
const debugLog = createDebug('explorbot:tester');
|
|
@@ -97,6 +97,22 @@ export class Tester extends TaskAgent {
|
|
|
97
97
|
this.explorer.getStateManager().clearHistory();
|
|
98
98
|
this.resetFailureCount();
|
|
99
99
|
this.pilot?.reset();
|
|
100
|
+
const requestStore = this.explorer.getRequestStore();
|
|
101
|
+
requestStore?.clear();
|
|
102
|
+
const offFailedRequest = requestStore?.onFailedRequest((r) => {
|
|
103
|
+
task.addNote(`Network error: ${r.method} ${r.path} → ${r.status}`, TestResult.FAILED);
|
|
104
|
+
});
|
|
105
|
+
const page = this.explorer.playwrightHelper?.page;
|
|
106
|
+
const onPageError = (err) => {
|
|
107
|
+
task.addNote(`Console error: ${err.message}`, TestResult.FAILED);
|
|
108
|
+
};
|
|
109
|
+
const onConsoleMessage = (msg) => {
|
|
110
|
+
if (msg.type() !== 'error')
|
|
111
|
+
return;
|
|
112
|
+
task.addNote(`Console error: ${msg.text()}`, TestResult.FAILED);
|
|
113
|
+
};
|
|
114
|
+
page?.on('pageerror', onPageError);
|
|
115
|
+
page?.on('console', onConsoleMessage);
|
|
100
116
|
const initialState = ActionResult.fromState(state);
|
|
101
117
|
const conversation = this.provider.startConversation(this.getSystemMessage(), 'tester');
|
|
102
118
|
this.currentConversation = conversation;
|
|
@@ -295,6 +311,9 @@ export class Tester extends TaskAgent {
|
|
|
295
311
|
}
|
|
296
312
|
await this.getQuartermaster().analyzeSession(task, initialState, conversation);
|
|
297
313
|
offStateChange();
|
|
314
|
+
offFailedRequest?.();
|
|
315
|
+
page?.off('pageerror', onPageError);
|
|
316
|
+
page?.off('console', onConsoleMessage);
|
|
298
317
|
await this.finishTest(task);
|
|
299
318
|
await this.explorer.stopTest(task, {
|
|
300
319
|
startUrl: task.startUrl,
|
|
@@ -606,6 +625,8 @@ export class Tester extends TaskAgent {
|
|
|
606
625
|
When creating or editing items via form() or type() you should include ${task.sessionName} in the value (if it is not restricted by the application logic)
|
|
607
626
|
Initial page URL: ${actionResult.url}
|
|
608
627
|
|
|
628
|
+
${protectionRule}
|
|
629
|
+
|
|
609
630
|
${this.buildDeletionScope(task)}
|
|
610
631
|
|
|
611
632
|
${this.buildAvailableFiles()}
|
package/dist/src/utils/aria.js
CHANGED
|
@@ -394,7 +394,7 @@ export const detectFocusArea = (snapshot) => {
|
|
|
394
394
|
if (result)
|
|
395
395
|
return result;
|
|
396
396
|
const fallback = findOverlayByCloseButton(nodes);
|
|
397
|
-
if (fallback
|
|
397
|
+
if (fallback?.name)
|
|
398
398
|
return fallback;
|
|
399
399
|
return { detected: false, type: null, name: null };
|
|
400
400
|
};
|
package/package.json
CHANGED
package/src/ai/rules.ts
CHANGED
|
@@ -135,7 +135,15 @@ export const fileUploadRule = dedent`
|
|
|
135
135
|
export const protectionRule = dedent`
|
|
136
136
|
<important>
|
|
137
137
|
Do not sign out current user of the application.
|
|
138
|
-
Do not change current user account settings
|
|
138
|
+
Do not change current user account settings.
|
|
139
|
+
|
|
140
|
+
Pre-existing data on the page belongs to the application, not the test.
|
|
141
|
+
Items that were not created inside the current test scenario must not be deleted, removed, emptied, reset, archived, or otherwise destroyed.
|
|
142
|
+
If a scenario needs to verify destructive behaviour, the same scenario must first create a disposable target and then destroy that specific target — never operate on data that was already there when the test started.
|
|
143
|
+
|
|
144
|
+
The resource that the current page URL represents is "under test".
|
|
145
|
+
The test must not destroy the resource it is running against — doing so invalidates every subsequent scenario that starts on the same URL.
|
|
146
|
+
Do not propose or perform delete/remove/archive actions on the entity that owns the current URL; propose such actions only on disposable children created within the scenario itself.
|
|
139
147
|
</important>
|
|
140
148
|
`;
|
|
141
149
|
|
package/src/ai/tester.ts
CHANGED
|
@@ -17,13 +17,13 @@ import { codeToMarkdown } from '../utils/html.ts';
|
|
|
17
17
|
import { createDebug, tag } from '../utils/logger.ts';
|
|
18
18
|
import { loop } from '../utils/loop.ts';
|
|
19
19
|
import type { Agent } from './agent.ts';
|
|
20
|
+
import type { Captain } from './captain.ts';
|
|
20
21
|
import type { Conversation } from './conversation.ts';
|
|
21
22
|
import { Navigator } from './navigator.ts';
|
|
22
|
-
import type { Captain } from './captain.ts';
|
|
23
23
|
import type { Pilot } from './pilot.ts';
|
|
24
24
|
import { Provider } from './provider.ts';
|
|
25
25
|
import { Researcher } from './researcher.ts';
|
|
26
|
-
import { actionRule, focusedElementRule, locatorRule, multipleTabsRule, sectionContextRule } from './rules.ts';
|
|
26
|
+
import { actionRule, focusedElementRule, locatorRule, multipleTabsRule, protectionRule, sectionContextRule } from './rules.ts';
|
|
27
27
|
import { TaskAgent } from './task-agent.ts';
|
|
28
28
|
import { createCodeceptJSTools, createSpecialContextTools } from './tools.ts';
|
|
29
29
|
|
|
@@ -125,6 +125,23 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
125
125
|
this.resetFailureCount();
|
|
126
126
|
this.pilot?.reset();
|
|
127
127
|
|
|
128
|
+
const requestStore = this.explorer.getRequestStore();
|
|
129
|
+
requestStore?.clear();
|
|
130
|
+
const offFailedRequest = requestStore?.onFailedRequest((r) => {
|
|
131
|
+
task.addNote(`Network error: ${r.method} ${r.path} → ${r.status}`, TestResult.FAILED);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const page = this.explorer.playwrightHelper?.page;
|
|
135
|
+
const onPageError = (err: Error) => {
|
|
136
|
+
task.addNote(`Console error: ${err.message}`, TestResult.FAILED);
|
|
137
|
+
};
|
|
138
|
+
const onConsoleMessage = (msg: any) => {
|
|
139
|
+
if (msg.type() !== 'error') return;
|
|
140
|
+
task.addNote(`Console error: ${msg.text()}`, TestResult.FAILED);
|
|
141
|
+
};
|
|
142
|
+
page?.on('pageerror', onPageError);
|
|
143
|
+
page?.on('console', onConsoleMessage);
|
|
144
|
+
|
|
128
145
|
const initialState = ActionResult.fromState(state);
|
|
129
146
|
|
|
130
147
|
const conversation = this.provider.startConversation(this.getSystemMessage(), 'tester');
|
|
@@ -355,6 +372,9 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
355
372
|
await this.getQuartermaster().analyzeSession(task, initialState, conversation);
|
|
356
373
|
|
|
357
374
|
offStateChange();
|
|
375
|
+
offFailedRequest?.();
|
|
376
|
+
page?.off('pageerror', onPageError);
|
|
377
|
+
page?.off('console', onConsoleMessage);
|
|
358
378
|
await this.finishTest(task);
|
|
359
379
|
await this.explorer.stopTest(task, {
|
|
360
380
|
startUrl: task.startUrl,
|
|
@@ -691,6 +711,8 @@ export class Tester extends TaskAgent implements Agent {
|
|
|
691
711
|
When creating or editing items via form() or type() you should include ${task.sessionName} in the value (if it is not restricted by the application logic)
|
|
692
712
|
Initial page URL: ${actionResult.url}
|
|
693
713
|
|
|
714
|
+
${protectionRule}
|
|
715
|
+
|
|
694
716
|
${this.buildDeletionScope(task)}
|
|
695
717
|
|
|
696
718
|
${this.buildAvailableFiles()}
|
package/src/utils/aria.ts
CHANGED
|
@@ -421,7 +421,7 @@ export const detectFocusArea = (snapshot: string | null): FocusAreaResult => {
|
|
|
421
421
|
if (result) return result;
|
|
422
422
|
|
|
423
423
|
const fallback = findOverlayByCloseButton(nodes);
|
|
424
|
-
if (fallback
|
|
424
|
+
if (fallback?.name) return fallback;
|
|
425
425
|
|
|
426
426
|
return { detected: false, type: null, name: null };
|
|
427
427
|
};
|