auto-feedback 0.1.0
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/README.md +180 -0
- package/build/capture/console-collector.d.ts +16 -0
- package/build/capture/console-collector.js +43 -0
- package/build/capture/error-collector.d.ts +15 -0
- package/build/capture/error-collector.js +47 -0
- package/build/capture/network-collector.d.ts +16 -0
- package/build/capture/network-collector.js +76 -0
- package/build/capture/process-collector.d.ts +16 -0
- package/build/capture/process-collector.js +48 -0
- package/build/capture/types.d.ts +61 -0
- package/build/capture/types.js +5 -0
- package/build/index.d.ts +6 -0
- package/build/index.js +41 -0
- package/build/interaction/selectors.d.ts +26 -0
- package/build/interaction/selectors.js +84 -0
- package/build/interaction/types.d.ts +56 -0
- package/build/interaction/types.js +5 -0
- package/build/process/cleanup.d.ts +23 -0
- package/build/process/cleanup.js +50 -0
- package/build/process/launcher.d.ts +22 -0
- package/build/process/launcher.js +54 -0
- package/build/process/monitor.d.ts +14 -0
- package/build/process/monitor.js +67 -0
- package/build/process/types.d.ts +84 -0
- package/build/process/types.js +5 -0
- package/build/screenshot/auto-capture.d.ts +14 -0
- package/build/screenshot/auto-capture.js +38 -0
- package/build/screenshot/capture.d.ts +21 -0
- package/build/screenshot/capture.js +48 -0
- package/build/screenshot/optimize.d.ts +19 -0
- package/build/screenshot/optimize.js +28 -0
- package/build/screenshot/types.d.ts +43 -0
- package/build/screenshot/types.js +4 -0
- package/build/server.d.ts +10 -0
- package/build/server.js +18 -0
- package/build/session-manager.d.ts +119 -0
- package/build/session-manager.js +284 -0
- package/build/tools/check-port.d.ts +10 -0
- package/build/tools/check-port.js +40 -0
- package/build/tools/click-element.d.ts +13 -0
- package/build/tools/click-element.js +118 -0
- package/build/tools/get-console-logs.d.ts +7 -0
- package/build/tools/get-console-logs.js +55 -0
- package/build/tools/get-element-state.d.ts +14 -0
- package/build/tools/get-element-state.js +116 -0
- package/build/tools/get-errors.d.ts +7 -0
- package/build/tools/get-errors.js +40 -0
- package/build/tools/get-network-logs.d.ts +7 -0
- package/build/tools/get-network-logs.js +58 -0
- package/build/tools/get-process-output.d.ts +7 -0
- package/build/tools/get-process-output.js +55 -0
- package/build/tools/get-screenshot.d.ts +7 -0
- package/build/tools/get-screenshot.js +32 -0
- package/build/tools/index.d.ts +9 -0
- package/build/tools/index.js +117 -0
- package/build/tools/launch-electron.d.ts +13 -0
- package/build/tools/launch-electron.js +97 -0
- package/build/tools/launch-web-server.d.ts +13 -0
- package/build/tools/launch-web-server.js +88 -0
- package/build/tools/launch-windows-exe.d.ts +13 -0
- package/build/tools/launch-windows-exe.js +81 -0
- package/build/tools/navigate.d.ts +13 -0
- package/build/tools/navigate.js +137 -0
- package/build/tools/run-workflow.d.ts +14 -0
- package/build/tools/run-workflow.js +207 -0
- package/build/tools/screenshot-desktop.d.ts +13 -0
- package/build/tools/screenshot-desktop.js +80 -0
- package/build/tools/screenshot-electron.d.ts +13 -0
- package/build/tools/screenshot-electron.js +72 -0
- package/build/tools/screenshot-web.d.ts +13 -0
- package/build/tools/screenshot-web.js +129 -0
- package/build/tools/stop-process.d.ts +14 -0
- package/build/tools/stop-process.js +41 -0
- package/build/tools/type-text.d.ts +13 -0
- package/build/tools/type-text.js +137 -0
- package/build/tools/wait-for-element.d.ts +14 -0
- package/build/tools/wait-for-element.js +93 -0
- package/build/types/index.d.ts +31 -0
- package/build/types/index.js +4 -0
- package/build/utils/errors.d.ts +26 -0
- package/build/utils/errors.js +62 -0
- package/build/utils/shutdown.d.ts +16 -0
- package/build/utils/shutdown.js +34 -0
- package/build/workflow/assertions.d.ts +25 -0
- package/build/workflow/assertions.js +326 -0
- package/build/workflow/executor.d.ts +34 -0
- package/build/workflow/executor.js +269 -0
- package/build/workflow/types.d.ts +95 -0
- package/build/workflow/types.js +6 -0
- package/package.json +36 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured error formatting for tool results
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Create a tool error response with structured information
|
|
6
|
+
* @param message Error message
|
|
7
|
+
* @param context Optional context about what was being attempted
|
|
8
|
+
* @param suggestedFix Optional suggestion for how to fix the issue
|
|
9
|
+
* @returns ToolResult with isError: true
|
|
10
|
+
*/
|
|
11
|
+
export function createToolError(message, context, suggestedFix) {
|
|
12
|
+
let text = `Error: ${message}`;
|
|
13
|
+
if (context) {
|
|
14
|
+
text += `\nContext: ${context}`;
|
|
15
|
+
}
|
|
16
|
+
if (suggestedFix) {
|
|
17
|
+
text += `\nSuggested fix: ${suggestedFix}`;
|
|
18
|
+
}
|
|
19
|
+
return {
|
|
20
|
+
content: [{ type: "text", text }],
|
|
21
|
+
isError: true,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create a successful tool result
|
|
26
|
+
* @param data Data to serialize as JSON
|
|
27
|
+
* @returns ToolResult with isError: false
|
|
28
|
+
*/
|
|
29
|
+
export function createToolResult(data) {
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: "text",
|
|
34
|
+
text: JSON.stringify(data, null, 2),
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
isError: false,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a screenshot tool result with text metadata and image content
|
|
42
|
+
* @param metadata Metadata to include as JSON text
|
|
43
|
+
* @param imageBase64 Base64-encoded image data
|
|
44
|
+
* @param mimeType Image MIME type (default: 'image/webp')
|
|
45
|
+
* @returns ToolResult with text + image content
|
|
46
|
+
*/
|
|
47
|
+
export function createScreenshotResult(metadata, imageBase64, mimeType = "image/webp") {
|
|
48
|
+
return {
|
|
49
|
+
content: [
|
|
50
|
+
{
|
|
51
|
+
type: "text",
|
|
52
|
+
text: JSON.stringify(metadata, null, 2),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: "image",
|
|
56
|
+
data: imageBase64,
|
|
57
|
+
mimeType,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
isError: false,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graceful shutdown coordinator with cleanup handler registry
|
|
3
|
+
*/
|
|
4
|
+
export declare class ShutdownManager {
|
|
5
|
+
private cleanupHandlers;
|
|
6
|
+
private isShuttingDown;
|
|
7
|
+
/**
|
|
8
|
+
* Register a cleanup handler to run during shutdown
|
|
9
|
+
*/
|
|
10
|
+
register(handler: () => Promise<void>): void;
|
|
11
|
+
/**
|
|
12
|
+
* Execute all cleanup handlers and exit process
|
|
13
|
+
* Safe to call multiple times - subsequent calls are ignored
|
|
14
|
+
*/
|
|
15
|
+
cleanup(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graceful shutdown coordinator with cleanup handler registry
|
|
3
|
+
*/
|
|
4
|
+
export class ShutdownManager {
|
|
5
|
+
cleanupHandlers = [];
|
|
6
|
+
isShuttingDown = false;
|
|
7
|
+
/**
|
|
8
|
+
* Register a cleanup handler to run during shutdown
|
|
9
|
+
*/
|
|
10
|
+
register(handler) {
|
|
11
|
+
this.cleanupHandlers.push(handler);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Execute all cleanup handlers and exit process
|
|
15
|
+
* Safe to call multiple times - subsequent calls are ignored
|
|
16
|
+
*/
|
|
17
|
+
async cleanup() {
|
|
18
|
+
if (this.isShuttingDown) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
this.isShuttingDown = true;
|
|
22
|
+
console.error("Shutting down...");
|
|
23
|
+
// Run all handlers in parallel with individual try/catch
|
|
24
|
+
const results = await Promise.allSettled(this.cleanupHandlers.map((handler) => handler()));
|
|
25
|
+
// Log any handler failures
|
|
26
|
+
results.forEach((result, index) => {
|
|
27
|
+
if (result.status === "rejected") {
|
|
28
|
+
console.error(`Cleanup handler ${index} failed:`, result.reason);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
console.error("Shutdown complete");
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assertion evaluation for workflow assert steps.
|
|
3
|
+
* Queries element state via Playwright locator methods and returns
|
|
4
|
+
* structured pass/fail results.
|
|
5
|
+
*/
|
|
6
|
+
import type { Page } from "playwright";
|
|
7
|
+
import type { WorkflowStep } from "./types.js";
|
|
8
|
+
/**
|
|
9
|
+
* Structured result of an assertion evaluation.
|
|
10
|
+
*/
|
|
11
|
+
export interface AssertionResult {
|
|
12
|
+
passed: boolean;
|
|
13
|
+
assertType: string;
|
|
14
|
+
selector: string;
|
|
15
|
+
expected: string | null;
|
|
16
|
+
actual: string | null;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Evaluate an assertion step against a Playwright page.
|
|
21
|
+
*
|
|
22
|
+
* Assertion failures produce { passed: false } results, NOT thrown exceptions.
|
|
23
|
+
* Only unexpected errors (page crash, browser disconnect) propagate as throws.
|
|
24
|
+
*/
|
|
25
|
+
export declare function evaluateAssertion(page: Page, step: WorkflowStep, timeout: number): Promise<AssertionResult>;
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assertion evaluation for workflow assert steps.
|
|
3
|
+
* Queries element state via Playwright locator methods and returns
|
|
4
|
+
* structured pass/fail results.
|
|
5
|
+
*/
|
|
6
|
+
import { resolveSelector } from "../interaction/selectors.js";
|
|
7
|
+
/**
|
|
8
|
+
* Evaluate an assertion step against a Playwright page.
|
|
9
|
+
*
|
|
10
|
+
* Assertion failures produce { passed: false } results, NOT thrown exceptions.
|
|
11
|
+
* Only unexpected errors (page crash, browser disconnect) propagate as throws.
|
|
12
|
+
*/
|
|
13
|
+
export async function evaluateAssertion(page, step, timeout) {
|
|
14
|
+
const base = {
|
|
15
|
+
assertType: step.assertType,
|
|
16
|
+
selector: step.selector,
|
|
17
|
+
};
|
|
18
|
+
const locator = resolveSelector(page, step.selector);
|
|
19
|
+
switch (step.assertType) {
|
|
20
|
+
case "exists": {
|
|
21
|
+
const count = await locator.count();
|
|
22
|
+
const passed = count > 0;
|
|
23
|
+
return {
|
|
24
|
+
...base,
|
|
25
|
+
passed,
|
|
26
|
+
expected: "element exists in DOM",
|
|
27
|
+
actual: count > 0 ? `found (${count} match${count > 1 ? "es" : ""})` : "not found",
|
|
28
|
+
message: passed
|
|
29
|
+
? `PASS: Element "${step.selector}" exists (${count} match${count > 1 ? "es" : ""})`
|
|
30
|
+
: `FAIL: Element "${step.selector}" does not exist`,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
case "not-exists": {
|
|
34
|
+
const count = await locator.count();
|
|
35
|
+
const passed = count === 0;
|
|
36
|
+
return {
|
|
37
|
+
...base,
|
|
38
|
+
passed,
|
|
39
|
+
expected: "element does not exist in DOM",
|
|
40
|
+
actual: count === 0 ? "not found" : `found (${count} match${count > 1 ? "es" : ""})`,
|
|
41
|
+
message: passed
|
|
42
|
+
? `PASS: Element "${step.selector}" does not exist`
|
|
43
|
+
: `FAIL: Element "${step.selector}" exists (${count} match${count > 1 ? "es" : ""})`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
case "visible": {
|
|
47
|
+
try {
|
|
48
|
+
await locator.waitFor({ state: "attached", timeout });
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return {
|
|
52
|
+
...base,
|
|
53
|
+
passed: false,
|
|
54
|
+
expected: "element is visible",
|
|
55
|
+
actual: "element not found in DOM",
|
|
56
|
+
message: `FAIL: Element "${step.selector}" not found in DOM (timeout waiting for attachment)`,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const visible = await locator.isVisible();
|
|
60
|
+
return {
|
|
61
|
+
...base,
|
|
62
|
+
passed: visible,
|
|
63
|
+
expected: "element is visible",
|
|
64
|
+
actual: visible ? "visible" : "hidden",
|
|
65
|
+
message: visible
|
|
66
|
+
? `PASS: Element "${step.selector}" is visible`
|
|
67
|
+
: `FAIL: Element "${step.selector}" is hidden`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
case "hidden": {
|
|
71
|
+
const count = await locator.count();
|
|
72
|
+
if (count === 0) {
|
|
73
|
+
return {
|
|
74
|
+
...base,
|
|
75
|
+
passed: true,
|
|
76
|
+
expected: "element is hidden",
|
|
77
|
+
actual: "not in DOM (hidden)",
|
|
78
|
+
message: `PASS: Element "${step.selector}" is not in DOM (counts as hidden)`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const visible = await locator.isVisible();
|
|
82
|
+
const passed = !visible;
|
|
83
|
+
return {
|
|
84
|
+
...base,
|
|
85
|
+
passed,
|
|
86
|
+
expected: "element is hidden",
|
|
87
|
+
actual: visible ? "visible" : "hidden",
|
|
88
|
+
message: passed
|
|
89
|
+
? `PASS: Element "${step.selector}" is hidden`
|
|
90
|
+
: `FAIL: Element "${step.selector}" is visible`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
case "text-equals": {
|
|
94
|
+
try {
|
|
95
|
+
await locator.waitFor({ state: "attached", timeout });
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return {
|
|
99
|
+
...base,
|
|
100
|
+
passed: false,
|
|
101
|
+
expected: `text equals "${step.expected}"`,
|
|
102
|
+
actual: "element not found in DOM",
|
|
103
|
+
message: `FAIL: Element "${step.selector}" not found in DOM`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const text = (await locator.innerText({ timeout })).trim();
|
|
107
|
+
const passed = text === step.expected;
|
|
108
|
+
return {
|
|
109
|
+
...base,
|
|
110
|
+
passed,
|
|
111
|
+
expected: `text equals "${step.expected}"`,
|
|
112
|
+
actual: `"${text}"`,
|
|
113
|
+
message: passed
|
|
114
|
+
? `PASS: Text of "${step.selector}" equals "${step.expected}"`
|
|
115
|
+
: `FAIL: Text of "${step.selector}" is "${text}", expected "${step.expected}"`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
case "text-contains": {
|
|
119
|
+
try {
|
|
120
|
+
await locator.waitFor({ state: "attached", timeout });
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return {
|
|
124
|
+
...base,
|
|
125
|
+
passed: false,
|
|
126
|
+
expected: `text contains "${step.expected}"`,
|
|
127
|
+
actual: "element not found in DOM",
|
|
128
|
+
message: `FAIL: Element "${step.selector}" not found in DOM`,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const text = await locator.innerText({ timeout });
|
|
132
|
+
const passed = text.includes(step.expected ?? "");
|
|
133
|
+
return {
|
|
134
|
+
...base,
|
|
135
|
+
passed,
|
|
136
|
+
expected: `text contains "${step.expected}"`,
|
|
137
|
+
actual: `"${text}"`,
|
|
138
|
+
message: passed
|
|
139
|
+
? `PASS: Text of "${step.selector}" contains "${step.expected}"`
|
|
140
|
+
: `FAIL: Text of "${step.selector}" is "${text}", does not contain "${step.expected}"`,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
case "has-attribute": {
|
|
144
|
+
try {
|
|
145
|
+
await locator.waitFor({ state: "attached", timeout });
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
return {
|
|
149
|
+
...base,
|
|
150
|
+
passed: false,
|
|
151
|
+
expected: `has attribute "${step.attribute}"`,
|
|
152
|
+
actual: "element not found in DOM",
|
|
153
|
+
message: `FAIL: Element "${step.selector}" not found in DOM`,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const value = await locator.getAttribute(step.attribute, { timeout });
|
|
157
|
+
const passed = value !== null;
|
|
158
|
+
return {
|
|
159
|
+
...base,
|
|
160
|
+
passed,
|
|
161
|
+
expected: `has attribute "${step.attribute}"`,
|
|
162
|
+
actual: passed ? `attribute "${step.attribute}" present (value: "${value}")` : `attribute "${step.attribute}" not found`,
|
|
163
|
+
message: passed
|
|
164
|
+
? `PASS: Element "${step.selector}" has attribute "${step.attribute}"`
|
|
165
|
+
: `FAIL: Element "${step.selector}" does not have attribute "${step.attribute}"`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
case "attribute-equals": {
|
|
169
|
+
try {
|
|
170
|
+
await locator.waitFor({ state: "attached", timeout });
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
return {
|
|
174
|
+
...base,
|
|
175
|
+
passed: false,
|
|
176
|
+
expected: `attribute "${step.attribute}" equals "${step.expected}"`,
|
|
177
|
+
actual: "element not found in DOM",
|
|
178
|
+
message: `FAIL: Element "${step.selector}" not found in DOM`,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const value = await locator.getAttribute(step.attribute, { timeout });
|
|
182
|
+
const passed = value === step.expected;
|
|
183
|
+
return {
|
|
184
|
+
...base,
|
|
185
|
+
passed,
|
|
186
|
+
expected: `attribute "${step.attribute}" equals "${step.expected}"`,
|
|
187
|
+
actual: value !== null ? `"${value}"` : "attribute not found",
|
|
188
|
+
message: passed
|
|
189
|
+
? `PASS: Attribute "${step.attribute}" of "${step.selector}" equals "${step.expected}"`
|
|
190
|
+
: `FAIL: Attribute "${step.attribute}" of "${step.selector}" is ${value !== null ? `"${value}"` : "not found"}, expected "${step.expected}"`,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
case "enabled": {
|
|
194
|
+
try {
|
|
195
|
+
await locator.waitFor({ state: "attached", timeout });
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
return {
|
|
199
|
+
...base,
|
|
200
|
+
passed: false,
|
|
201
|
+
expected: "element is enabled",
|
|
202
|
+
actual: "element not found in DOM",
|
|
203
|
+
message: `FAIL: Element "${step.selector}" not found in DOM`,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
const enabled = await locator.isEnabled({ timeout });
|
|
207
|
+
return {
|
|
208
|
+
...base,
|
|
209
|
+
passed: enabled,
|
|
210
|
+
expected: "element is enabled",
|
|
211
|
+
actual: enabled ? "enabled" : "disabled",
|
|
212
|
+
message: enabled
|
|
213
|
+
? `PASS: Element "${step.selector}" is enabled`
|
|
214
|
+
: `FAIL: Element "${step.selector}" is disabled`,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
case "disabled": {
|
|
218
|
+
try {
|
|
219
|
+
await locator.waitFor({ state: "attached", timeout });
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
return {
|
|
223
|
+
...base,
|
|
224
|
+
passed: false,
|
|
225
|
+
expected: "element is disabled",
|
|
226
|
+
actual: "element not found in DOM",
|
|
227
|
+
message: `FAIL: Element "${step.selector}" not found in DOM`,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
const enabled = await locator.isEnabled({ timeout });
|
|
231
|
+
const passed = !enabled;
|
|
232
|
+
return {
|
|
233
|
+
...base,
|
|
234
|
+
passed,
|
|
235
|
+
expected: "element is disabled",
|
|
236
|
+
actual: enabled ? "enabled" : "disabled",
|
|
237
|
+
message: passed
|
|
238
|
+
? `PASS: Element "${step.selector}" is disabled`
|
|
239
|
+
: `FAIL: Element "${step.selector}" is enabled`,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
case "checked": {
|
|
243
|
+
try {
|
|
244
|
+
await locator.waitFor({ state: "attached", timeout });
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
return {
|
|
248
|
+
...base,
|
|
249
|
+
passed: false,
|
|
250
|
+
expected: "element is checked",
|
|
251
|
+
actual: "element not found in DOM",
|
|
252
|
+
message: `FAIL: Element "${step.selector}" not found in DOM`,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
const checked = await locator.isChecked({ timeout });
|
|
256
|
+
return {
|
|
257
|
+
...base,
|
|
258
|
+
passed: checked,
|
|
259
|
+
expected: "element is checked",
|
|
260
|
+
actual: checked ? "checked" : "not checked",
|
|
261
|
+
message: checked
|
|
262
|
+
? `PASS: Element "${step.selector}" is checked`
|
|
263
|
+
: `FAIL: Element "${step.selector}" is not checked`,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
case "not-checked": {
|
|
267
|
+
try {
|
|
268
|
+
await locator.waitFor({ state: "attached", timeout });
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return {
|
|
272
|
+
...base,
|
|
273
|
+
passed: false,
|
|
274
|
+
expected: "element is not checked",
|
|
275
|
+
actual: "element not found in DOM",
|
|
276
|
+
message: `FAIL: Element "${step.selector}" not found in DOM`,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
const checked = await locator.isChecked({ timeout });
|
|
280
|
+
const passed = !checked;
|
|
281
|
+
return {
|
|
282
|
+
...base,
|
|
283
|
+
passed,
|
|
284
|
+
expected: "element is not checked",
|
|
285
|
+
actual: checked ? "checked" : "not checked",
|
|
286
|
+
message: passed
|
|
287
|
+
? `PASS: Element "${step.selector}" is not checked`
|
|
288
|
+
: `FAIL: Element "${step.selector}" is checked`,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
case "value-equals": {
|
|
292
|
+
try {
|
|
293
|
+
await locator.waitFor({ state: "attached", timeout });
|
|
294
|
+
}
|
|
295
|
+
catch {
|
|
296
|
+
return {
|
|
297
|
+
...base,
|
|
298
|
+
passed: false,
|
|
299
|
+
expected: `value equals "${step.expected}"`,
|
|
300
|
+
actual: "element not found in DOM",
|
|
301
|
+
message: `FAIL: Element "${step.selector}" not found in DOM`,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
const value = await locator.inputValue({ timeout });
|
|
305
|
+
const passed = value === step.expected;
|
|
306
|
+
return {
|
|
307
|
+
...base,
|
|
308
|
+
passed,
|
|
309
|
+
expected: `value equals "${step.expected}"`,
|
|
310
|
+
actual: `"${value}"`,
|
|
311
|
+
message: passed
|
|
312
|
+
? `PASS: Value of "${step.selector}" equals "${step.expected}"`
|
|
313
|
+
: `FAIL: Value of "${step.selector}" is "${value}", expected "${step.expected}"`,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
default: {
|
|
317
|
+
return {
|
|
318
|
+
...base,
|
|
319
|
+
passed: false,
|
|
320
|
+
expected: null,
|
|
321
|
+
actual: null,
|
|
322
|
+
message: `Unknown assertion type: ${step.assertType}`,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow step executor
|
|
3
|
+
* Sequentially runs actions against a Playwright page with per-step
|
|
4
|
+
* screenshot capture and console/error log delta tracking.
|
|
5
|
+
*
|
|
6
|
+
* Stop-on-error semantics: failed steps still capture screenshot and
|
|
7
|
+
* log deltas for debugging context, then execution halts.
|
|
8
|
+
*/
|
|
9
|
+
import type { Page } from "playwright";
|
|
10
|
+
import type { SessionManager } from "../session-manager.js";
|
|
11
|
+
import type { WorkflowStep, WorkflowResult } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Validate a workflow step has all required fields for its action type.
|
|
14
|
+
*
|
|
15
|
+
* @param step - The workflow step to validate
|
|
16
|
+
* @param index - Zero-based step index (for error messages)
|
|
17
|
+
* @returns null if valid, or a descriptive error string
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateStep(step: WorkflowStep, index: number): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Execute a workflow — sequential steps against a Playwright page.
|
|
22
|
+
*
|
|
23
|
+
* Each step: validate -> execute action -> capture screenshot -> capture log deltas.
|
|
24
|
+
* On error: capture screenshot + log deltas for debugging, then stop.
|
|
25
|
+
*
|
|
26
|
+
* @returns WorkflowResult with per-step results, counts, and optional failedStep index
|
|
27
|
+
*/
|
|
28
|
+
export declare function executeWorkflow(params: {
|
|
29
|
+
page: Page;
|
|
30
|
+
steps: WorkflowStep[];
|
|
31
|
+
sessionManager: SessionManager;
|
|
32
|
+
sessionId: string;
|
|
33
|
+
pageIdentifier: string;
|
|
34
|
+
}): Promise<WorkflowResult>;
|