@cotestdev/ai-runner 0.0.6 → 0.0.7
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/playwright.yml +27 -0
- package/dist/agents/playwright-executor.d.ts +3 -1
- package/dist/agents/playwright-executor.d.ts.map +1 -1
- package/dist/agents/playwright-executor.js +74 -18
- package/dist/agents/playwright-executor.js.map +1 -1
- package/dist/agents/tools/playwright-backend-adapter.d.ts.map +1 -1
- package/dist/agents/tools/playwright-backend-adapter.js +2 -1
- package/dist/agents/tools/playwright-backend-adapter.js.map +1 -1
- package/dist/runner.d.ts +7 -2
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +23 -12
- package/dist/runner.js.map +1 -1
- package/package.json +4 -3
- package/src/agents/playwright-executor.ts +50 -18
- package/src/agents/tools/playwright-backend-adapter.ts +2 -1
- package/src/agents/tools/playwright-mcp-types.d.ts +2 -1
- package/src/runner.ts +27 -14
- package/tests/agent/playwright.config.ts +32 -0
- package/tests/agent/test-heal-agent.spec.ts +35 -47
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
name: Playwright Tests
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [ main, master ]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [ main, master ]
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
timeout-minutes: 60
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-node@v4
|
|
14
|
+
with:
|
|
15
|
+
node-version: lts/*
|
|
16
|
+
- name: Install dependencies
|
|
17
|
+
run: npm ci
|
|
18
|
+
- name: Install Playwright Browsers
|
|
19
|
+
run: npx playwright install --with-deps
|
|
20
|
+
- name: Run Playwright tests
|
|
21
|
+
run: npx playwright test
|
|
22
|
+
- uses: actions/upload-artifact@v4
|
|
23
|
+
if: ${{ !cancelled() }}
|
|
24
|
+
with:
|
|
25
|
+
name: playwright-report
|
|
26
|
+
path: playwright-report/
|
|
27
|
+
retention-days: 30
|
|
@@ -5,7 +5,8 @@ import type { HealContext } from '../types';
|
|
|
5
5
|
import { PlaywrightAgentState } from './types';
|
|
6
6
|
export declare class PlaywrightExecutor {
|
|
7
7
|
private state;
|
|
8
|
-
|
|
8
|
+
private signal;
|
|
9
|
+
constructor(state: PlaywrightAgentState, signal: AbortSignal);
|
|
9
10
|
private createPlaywrightTools;
|
|
10
11
|
/**
|
|
11
12
|
* Update original script and save to database (heal mode)
|
|
@@ -13,6 +14,7 @@ export declare class PlaywrightExecutor {
|
|
|
13
14
|
private updateOriginalScript;
|
|
14
15
|
execute(context: HealContext): Promise<void>;
|
|
15
16
|
private buildSystemPrompt;
|
|
17
|
+
private getSnapshotContext;
|
|
16
18
|
private buildUserMessage;
|
|
17
19
|
/**
|
|
18
20
|
* Extract and normalize content from model response
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-executor.d.ts","sourceRoot":"","sources":["../../src/agents/playwright-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"playwright-executor.d.ts","sourceRoot":"","sources":["../../src/agents/playwright-executor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAG/C,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,MAAM,CAAc;gBAEhB,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,WAAW;YAK9C,qBAAqB;IAuFnC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAKtB,OAAO,CAAC,OAAO,EAAE,WAAW;IA0DlC,OAAO,CAAC,iBAAiB;YAkBX,kBAAkB;IA0BhC,OAAO,CAAC,gBAAgB;IAmBxB;;;;SAIK;IACL,OAAO,CAAC,cAAc;CAuCvB"}
|
|
@@ -3,17 +3,48 @@
|
|
|
3
3
|
* Playwright Executor - Uses LangChain's createAgent API to implement ReAct loop
|
|
4
4
|
*/
|
|
5
5
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
6
|
-
var
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
9
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
40
|
exports.PlaywrightExecutor = void 0;
|
|
11
41
|
const langchain_1 = require("langchain");
|
|
12
42
|
const tools_1 = require("@langchain/core/tools");
|
|
13
|
-
const zod_1 =
|
|
43
|
+
const zod_1 = __importStar(require("zod"));
|
|
14
44
|
class PlaywrightExecutor {
|
|
15
|
-
constructor(state) {
|
|
45
|
+
constructor(state, signal) {
|
|
16
46
|
this.state = state;
|
|
47
|
+
this.signal = signal;
|
|
17
48
|
}
|
|
18
49
|
async createPlaywrightTools(healContext) {
|
|
19
50
|
const standardTools = this.state.playwrightGroup.getDefaultTools();
|
|
@@ -108,23 +139,33 @@ The complete fixed test script, leave empty if the step fails:
|
|
|
108
139
|
const page = this.state.page;
|
|
109
140
|
if (!page)
|
|
110
141
|
throw new Error('Missing page object');
|
|
142
|
+
// Check if already aborted
|
|
143
|
+
if (this.signal.aborted) {
|
|
144
|
+
throw new Error('Execution aborted');
|
|
145
|
+
}
|
|
111
146
|
const tools = await this.createPlaywrightTools(context);
|
|
112
147
|
const agent = (0, langchain_1.createAgent)({
|
|
113
148
|
model: this.state.model,
|
|
114
149
|
tools: tools,
|
|
115
|
-
systemPrompt: this.buildSystemPrompt()
|
|
150
|
+
systemPrompt: this.buildSystemPrompt(),
|
|
116
151
|
});
|
|
152
|
+
const snapshot = await this.getSnapshotContext();
|
|
117
153
|
const inputStream = {
|
|
118
154
|
messages: [{
|
|
119
155
|
role: 'user',
|
|
120
|
-
content:
|
|
121
|
-
}
|
|
156
|
+
content: this.buildUserMessage(context)
|
|
157
|
+
},
|
|
158
|
+
...snapshot || [],]
|
|
122
159
|
};
|
|
123
160
|
const stream = await agent.stream(inputStream, {
|
|
124
161
|
recursionLimit: 25,
|
|
125
162
|
streamMode: 'updates',
|
|
126
163
|
});
|
|
127
164
|
for await (const event of stream) {
|
|
165
|
+
// Check for abort signal
|
|
166
|
+
if (this.signal.aborted) {
|
|
167
|
+
throw new Error('Execution aborted');
|
|
168
|
+
}
|
|
128
169
|
// Process AI messages (thoughts and tool calls)
|
|
129
170
|
const messages = event.model_request?.messages || event.messages;
|
|
130
171
|
if (messages?.[0]?.type === 'ai') {
|
|
@@ -150,12 +191,9 @@ You are a Playwright test expert, skilled at analyzing and fixing errors in brow
|
|
|
150
191
|
## ** Critial Rules **
|
|
151
192
|
- You MUST run the step using tools first before fixing the script
|
|
152
193
|
- When you step finishes or fails, you MUST call 'finish_test' tool and set the 'isSuccess' parameter correctly.
|
|
153
|
-
- Use tools squentially
|
|
154
194
|
- Analyze the current step code, errors and page context and what variables are being set
|
|
155
195
|
- Make sure the fixed code is concise and consistent with the original code in logic and style
|
|
156
196
|
- CRITICAL: When calling 'finish_test', analyze the full test script to identify variables defined in the current step that are used in subsequent steps
|
|
157
|
-
- IMPORTANT: Extract the ACTUAL VALUES of those variables from your tool execution results (browser_snapshot output, element text, attributes, etc.)
|
|
158
|
-
- Include the extracted variable values in the 'variables' parameter of finish_test
|
|
159
197
|
|
|
160
198
|
## ** Constraints **
|
|
161
199
|
- NEVER set 'isSuccess' to true if the step goal is not fully achieved
|
|
@@ -163,12 +201,34 @@ You are a Playwright test expert, skilled at analyzing and fixing errors in brow
|
|
|
163
201
|
- Always check if variables assigned in the current step are referenced in later steps - if so, extract their actual values and include in 'variables'
|
|
164
202
|
`.trim();
|
|
165
203
|
}
|
|
166
|
-
async
|
|
204
|
+
async getSnapshotContext() {
|
|
205
|
+
const snapshot = await this.state.adapter.callTool('browser_snapshot', {});
|
|
206
|
+
if (snapshot.isError) {
|
|
207
|
+
this.state.logger.warn(`Failed to get browser snapshot: ${snapshot.error}`);
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
const callId = zod_1.uuid.toString();
|
|
211
|
+
const aiMessage = new langchain_1.AIMessage({
|
|
212
|
+
tool_calls: [
|
|
213
|
+
{
|
|
214
|
+
name: 'browser_snapshot',
|
|
215
|
+
id: callId,
|
|
216
|
+
args: {},
|
|
217
|
+
},
|
|
218
|
+
]
|
|
219
|
+
});
|
|
220
|
+
const toolMessage = new langchain_1.ToolMessage({
|
|
221
|
+
content: snapshot.content,
|
|
222
|
+
tool_call_id: callId,
|
|
223
|
+
name: 'browser_snapshot',
|
|
224
|
+
status: 'success',
|
|
225
|
+
});
|
|
226
|
+
return [aiMessage, toolMessage];
|
|
227
|
+
}
|
|
228
|
+
buildUserMessage(context) {
|
|
167
229
|
const variableList = Object.keys(this.state.variables).length > 0
|
|
168
230
|
? Object.keys(this.state.variables).map(v => ` - ${v}: ${JSON.stringify(this.state.variables.get(v))}`).join('\n')
|
|
169
231
|
: undefined;
|
|
170
|
-
const snapshot = await this.state.adapter.callTool('browser_snapshot', {});
|
|
171
|
-
const pageState = snapshot.isError ? '' : this.extractContent(snapshot);
|
|
172
232
|
return `
|
|
173
233
|
## Step Description: ${context.stepDescription}
|
|
174
234
|
|
|
@@ -181,9 +241,6 @@ ${context.stackTrace}
|
|
|
181
241
|
\`\`\`typescript
|
|
182
242
|
${this.state.originalScript}
|
|
183
243
|
\`\`\`
|
|
184
|
-
|
|
185
|
-
${pageState}
|
|
186
|
-
}
|
|
187
244
|
`.trim();
|
|
188
245
|
}
|
|
189
246
|
/**
|
|
@@ -219,7 +276,6 @@ ${pageState}
|
|
|
219
276
|
}
|
|
220
277
|
// If object, try to extract text field or convert to string
|
|
221
278
|
if (typeof content === 'object' && content !== null) {
|
|
222
|
-
``;
|
|
223
279
|
if ('text' in content) {
|
|
224
280
|
return content;
|
|
225
281
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-executor.js","sourceRoot":"","sources":["../../src/agents/playwright-executor.ts"],"names":[],"mappings":";AAAA;;GAEG;AACH,uDAAuD
|
|
1
|
+
{"version":3,"file":"playwright-executor.js","sourceRoot":"","sources":["../../src/agents/playwright-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-backend-adapter.d.ts","sourceRoot":"","sources":["../../../src/agents/tools/playwright-backend-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD,qBAAa,wBAAwB;IAEnC,OAAO,CAAC,OAAO,CAA2C;IAC1D,OAAO,CAAC,KAAK,CAA+B;WAE/B,MAAM,CAAC,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAOtF,QAAQ,IAAI,qBAAqB,EAAE;IAK7B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG;IAQxC;;;;;OAKG;IAEH,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,wBAAwB;YAelB,UAAU;
|
|
1
|
+
{"version":3,"file":"playwright-backend-adapter.d.ts","sourceRoot":"","sources":["../../../src/agents/tools/playwright-backend-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjD,qBAAa,wBAAwB;IAEnC,OAAO,CAAC,OAAO,CAA2C;IAC1D,OAAO,CAAC,KAAK,CAA+B;WAE/B,MAAM,CAAC,cAAc,EAAE,cAAc,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAOtF,QAAQ,IAAI,qBAAqB,EAAE;IAK7B,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG;IAQxC;;;;;OAKG;IAEH,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,wBAAwB;YAelB,UAAU;YA4BV,oCAAoC;CAsBnD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-backend-adapter.js","sourceRoot":"","sources":["../../../src/agents/tools/playwright-backend-adapter.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH,iDAA8D;AAE9D,2CAA6B;AAK7B,MAAa,wBAAwB;IAArC;QAGU,UAAK,GAA4B,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"playwright-backend-adapter.js","sourceRoot":"","sources":["../../../src/agents/tools/playwright-backend-adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.d.ts
CHANGED
|
@@ -9,12 +9,17 @@ export declare class Runner {
|
|
|
9
9
|
private state;
|
|
10
10
|
private script?;
|
|
11
11
|
private variables;
|
|
12
|
+
private abortController;
|
|
12
13
|
private constructor();
|
|
13
|
-
static NewInstance(projectId: string, testCaseId: string): Runner;
|
|
14
|
+
static NewInstance(projectId: string, testCaseId: string, canHeal?: boolean): Runner;
|
|
15
|
+
stop(): void;
|
|
14
16
|
init(page: Page, context: BrowserContext, data?: Record<string, any>): Promise<void>;
|
|
15
17
|
private loadOriginalScript;
|
|
16
|
-
forEach(data: Record<string, any>[], callback: (index: number) => void): void;
|
|
17
18
|
runStep(description: string, fn: () => Promise<void>): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Remove ANSI color codes from string
|
|
21
|
+
*/
|
|
22
|
+
private removeAnsiCodes;
|
|
18
23
|
private isHealableError;
|
|
19
24
|
set(key: string, value: any): void;
|
|
20
25
|
get(key: string): any;
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAUvD,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,KAAK,CAAC,CAAoB;IAClC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,SAAS,CAA4C;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAUvD,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,KAAK,CAAC,CAAoB;IAClC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,KAAK,CAAmC;IAChD,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,SAAS,CAA4C;IAC7D,OAAO,CAAC,eAAe,CAAkB;IAEzC,OAAO;IAOP,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,OAAc,GAAG,MAAM;IAmC1F,IAAI;IAKE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAsB1F,OAAO,CAAC,kBAAkB;IAcpB,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAyC1D;;KAEC;IACD,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,eAAe;IAsBvB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAIlC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG;IAIrB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI5B,KAAK,IAAI,IAAI;IAIb,IAAI,IAAI,MAAM,EAAE;IAIhB,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI3B,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CA2B3F"}
|
package/dist/runner.js
CHANGED
|
@@ -14,9 +14,17 @@ class Runner {
|
|
|
14
14
|
this.config = config;
|
|
15
15
|
this.storageService = core_infra_1.StorageService.getInstance(config.projectId);
|
|
16
16
|
this.logger = new logger_1.Logger();
|
|
17
|
+
this.abortController = new AbortController();
|
|
17
18
|
}
|
|
18
|
-
static NewInstance(projectId, testCaseId) {
|
|
19
|
+
static NewInstance(projectId, testCaseId, canHeal = true) {
|
|
19
20
|
const runner = new Runner({ projectId, testCaseId });
|
|
21
|
+
runner.canHeal = canHeal;
|
|
22
|
+
if (!canHeal) {
|
|
23
|
+
return runner;
|
|
24
|
+
}
|
|
25
|
+
if (process.env.NOARTIK_CAN_HEAL === 'true') {
|
|
26
|
+
runner.canHeal = true;
|
|
27
|
+
}
|
|
20
28
|
try {
|
|
21
29
|
const script = runner.loadOriginalScript();
|
|
22
30
|
if (!script) {
|
|
@@ -40,6 +48,10 @@ class Runner {
|
|
|
40
48
|
}
|
|
41
49
|
return runner;
|
|
42
50
|
}
|
|
51
|
+
stop() {
|
|
52
|
+
this.abortController.abort();
|
|
53
|
+
this.logger.log('Runner execution aborted.');
|
|
54
|
+
}
|
|
43
55
|
async init(page, context, data) {
|
|
44
56
|
const adapter = await playwright_backend_adapter_1.PlaywrightBackendAdapter.create(context);
|
|
45
57
|
this.state = {
|
|
@@ -59,27 +71,19 @@ class Runner {
|
|
|
59
71
|
this.set(key, value);
|
|
60
72
|
}
|
|
61
73
|
}
|
|
62
|
-
this.executor = new playwright_executor_1.PlaywrightExecutor(this.state);
|
|
74
|
+
this.executor = new playwright_executor_1.PlaywrightExecutor(this.state, this.abortController.signal);
|
|
63
75
|
}
|
|
64
76
|
loadOriginalScript() {
|
|
65
77
|
if (!this.config.testCaseId) {
|
|
66
78
|
throw new Error('Cannot load original script: testCaseId is not provided');
|
|
67
79
|
}
|
|
68
|
-
this.logger.log(`Attempting to load script from database: ${this.config.
|
|
80
|
+
this.logger.log(`Attempting to load script from database: ${this.config.testCaseId}`);
|
|
69
81
|
const script = this.storageService.loadTestScript(this.config.testCaseId);
|
|
70
82
|
if (!script) {
|
|
71
83
|
throw new Error(`Cannot load original script: Test case not found in database (ID: ${this.config.testCaseId})`);
|
|
72
84
|
}
|
|
73
85
|
return script;
|
|
74
86
|
}
|
|
75
|
-
forEach(data, callback) {
|
|
76
|
-
for (const [index, item] of data.entries()) {
|
|
77
|
-
for (const [key, value] of Object.entries(item)) {
|
|
78
|
-
this.set(key, value);
|
|
79
|
-
}
|
|
80
|
-
callback(index);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
87
|
async runStep(description, fn) {
|
|
84
88
|
const startTime = Date.now();
|
|
85
89
|
const context = {
|
|
@@ -101,7 +105,7 @@ class Runner {
|
|
|
101
105
|
throw error;
|
|
102
106
|
}
|
|
103
107
|
context.error = error.message;
|
|
104
|
-
context.stackTrace = error.stack;
|
|
108
|
+
context.stackTrace = this.removeAnsiCodes(error.stack || '');
|
|
105
109
|
this.logger.error(`Step failed: ${description}, Error: ${context.error}`);
|
|
106
110
|
this.logger.log(`Agent will attempt to self-heal...`);
|
|
107
111
|
await this.executor.execute(context);
|
|
@@ -114,6 +118,13 @@ class Runner {
|
|
|
114
118
|
throw new Error(`original error: ${context.error}; heal error: ${context.healError}`);
|
|
115
119
|
}
|
|
116
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Remove ANSI color codes from string
|
|
123
|
+
*/
|
|
124
|
+
removeAnsiCodes(str) {
|
|
125
|
+
// Remove ANSI escape sequences (e.g., [31m, [39m, \x1b[31m)
|
|
126
|
+
return str.replace(/\x1b\[[0-9;]*m|\u001b\[[0-9;]*m|\[3[0-9]m|\[39m|\[0m/g, '');
|
|
127
|
+
}
|
|
117
128
|
isHealableError(err) {
|
|
118
129
|
// 方式1:枚举所有 Symbol 属性(最常见尝试)
|
|
119
130
|
const symbols = Object.getOwnPropertySymbols(err);
|
package/dist/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":";;;AAMA,sDAAoE;AACpE,2CAA0C;AAE1C,4CAAyC;AACzC,sEAAkE;AAClE,iEAA4D;AAC5D,0FAAqF;AAGrF,MAAa,MAAM;
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cotestdev/ai-runner",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "AI-powered self-healing SDK for Playwright test scripts",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -35,9 +35,10 @@
|
|
|
35
35
|
"langchain": "^1.2.10"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@playwright/test": "^1.
|
|
38
|
+
"@playwright/test": "^1.58.0",
|
|
39
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
39
40
|
"@types/node": "^20.0.0",
|
|
40
41
|
"ts-node": "^10.9.2",
|
|
41
42
|
"typescript": "^5.0.0"
|
|
42
43
|
}
|
|
43
|
-
}
|
|
44
|
+
}
|
|
@@ -3,17 +3,20 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
5
|
|
|
6
|
-
import { createAgent } from 'langchain';
|
|
6
|
+
import { AIMessage, BaseMessage, createAgent, ToolMessage } from 'langchain';
|
|
7
7
|
import { DynamicStructuredTool } from '@langchain/core/tools';
|
|
8
8
|
import type { HealContext } from '../types';
|
|
9
|
-
import z from 'zod';
|
|
9
|
+
import z, { uuid } from 'zod';
|
|
10
10
|
import { PlaywrightAgentState } from './types';
|
|
11
|
+
import { uuid4 } from 'zod/v4/core/regexes.cjs';
|
|
11
12
|
|
|
12
13
|
export class PlaywrightExecutor {
|
|
13
14
|
private state: PlaywrightAgentState;
|
|
15
|
+
private signal: AbortSignal;
|
|
14
16
|
|
|
15
|
-
constructor(state: PlaywrightAgentState) {
|
|
17
|
+
constructor(state: PlaywrightAgentState, signal: AbortSignal) {
|
|
16
18
|
this.state = state;
|
|
19
|
+
this.signal = signal;
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
private async createPlaywrightTools(healContext: HealContext): Promise<DynamicStructuredTool[]> {
|
|
@@ -86,7 +89,7 @@ The complete fixed test script, leave empty if the step fails:
|
|
|
86
89
|
this.state.variables.set(key, value);
|
|
87
90
|
}
|
|
88
91
|
}
|
|
89
|
-
|
|
92
|
+
|
|
90
93
|
this.state.originalScript = params.repairedScript;
|
|
91
94
|
this.updateOriginalScript(params.repairedScript);
|
|
92
95
|
}
|
|
@@ -115,18 +118,26 @@ The complete fixed test script, leave empty if the step fails:
|
|
|
115
118
|
const page = this.state.page;
|
|
116
119
|
if (!page) throw new Error('Missing page object');
|
|
117
120
|
|
|
121
|
+
// Check if already aborted
|
|
122
|
+
if (this.signal.aborted) {
|
|
123
|
+
throw new Error('Execution aborted');
|
|
124
|
+
}
|
|
125
|
+
|
|
118
126
|
const tools = await this.createPlaywrightTools(context);
|
|
119
127
|
const agent = createAgent({
|
|
120
128
|
model: this.state.model!,
|
|
121
129
|
tools: tools,
|
|
122
|
-
systemPrompt: this.buildSystemPrompt()
|
|
130
|
+
systemPrompt: this.buildSystemPrompt(),
|
|
123
131
|
} as any);
|
|
124
132
|
|
|
133
|
+
const snapshot = await this.getSnapshotContext();
|
|
125
134
|
const inputStream = {
|
|
126
135
|
messages: [{
|
|
127
136
|
role: 'user' as const,
|
|
128
|
-
content:
|
|
129
|
-
}
|
|
137
|
+
content: this.buildUserMessage(context)
|
|
138
|
+
},
|
|
139
|
+
...snapshot || [],
|
|
140
|
+
]
|
|
130
141
|
};
|
|
131
142
|
|
|
132
143
|
const stream = await agent.stream(inputStream, {
|
|
@@ -135,6 +146,11 @@ The complete fixed test script, leave empty if the step fails:
|
|
|
135
146
|
});
|
|
136
147
|
|
|
137
148
|
for await (const event of stream) {
|
|
149
|
+
// Check for abort signal
|
|
150
|
+
if (this.signal.aborted) {
|
|
151
|
+
throw new Error('Execution aborted');
|
|
152
|
+
}
|
|
153
|
+
|
|
138
154
|
// Process AI messages (thoughts and tool calls)
|
|
139
155
|
const messages = event.model_request?.messages || event.messages;
|
|
140
156
|
if (messages?.[0]?.type === 'ai') {
|
|
@@ -163,12 +179,9 @@ You are a Playwright test expert, skilled at analyzing and fixing errors in brow
|
|
|
163
179
|
## ** Critial Rules **
|
|
164
180
|
- You MUST run the step using tools first before fixing the script
|
|
165
181
|
- When you step finishes or fails, you MUST call 'finish_test' tool and set the 'isSuccess' parameter correctly.
|
|
166
|
-
- Use tools squentially
|
|
167
182
|
- Analyze the current step code, errors and page context and what variables are being set
|
|
168
183
|
- Make sure the fixed code is concise and consistent with the original code in logic and style
|
|
169
184
|
- CRITICAL: When calling 'finish_test', analyze the full test script to identify variables defined in the current step that are used in subsequent steps
|
|
170
|
-
- IMPORTANT: Extract the ACTUAL VALUES of those variables from your tool execution results (browser_snapshot output, element text, attributes, etc.)
|
|
171
|
-
- Include the extracted variable values in the 'variables' parameter of finish_test
|
|
172
185
|
|
|
173
186
|
## ** Constraints **
|
|
174
187
|
- NEVER set 'isSuccess' to true if the step goal is not fully achieved
|
|
@@ -177,13 +190,36 @@ You are a Playwright test expert, skilled at analyzing and fixing errors in brow
|
|
|
177
190
|
`.trim();
|
|
178
191
|
}
|
|
179
192
|
|
|
180
|
-
private async
|
|
193
|
+
private async getSnapshotContext(): Promise<BaseMessage[] | undefined> {
|
|
194
|
+
const snapshot = await this.state.adapter.callTool('browser_snapshot', {});
|
|
195
|
+
if (snapshot.isError) {
|
|
196
|
+
this.state.logger.warn(`Failed to get browser snapshot: ${snapshot.error}`);
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const callId = uuid.toString();
|
|
201
|
+
const aiMessage = new AIMessage({
|
|
202
|
+
tool_calls: [
|
|
203
|
+
{
|
|
204
|
+
name: 'browser_snapshot',
|
|
205
|
+
id: callId,
|
|
206
|
+
args: {},
|
|
207
|
+
},
|
|
208
|
+
]
|
|
209
|
+
});
|
|
210
|
+
const toolMessage = new ToolMessage({
|
|
211
|
+
content: snapshot.content,
|
|
212
|
+
tool_call_id: callId,
|
|
213
|
+
name: 'browser_snapshot',
|
|
214
|
+
status: 'success',
|
|
215
|
+
});
|
|
216
|
+
return [aiMessage, toolMessage];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private buildUserMessage(context: HealContext): string {
|
|
181
220
|
const variableList = Object.keys(this.state.variables).length > 0
|
|
182
221
|
? Object.keys(this.state.variables).map(v => ` - ${v}: ${JSON.stringify(this.state.variables.get(v))}`).join('\n')
|
|
183
222
|
: undefined;
|
|
184
|
-
const snapshot = await this.state.adapter.callTool('browser_snapshot', {});
|
|
185
|
-
const pageState = snapshot.isError ? '' : this.extractContent(snapshot);
|
|
186
|
-
|
|
187
223
|
return `
|
|
188
224
|
## Step Description: ${context.stepDescription}
|
|
189
225
|
|
|
@@ -196,9 +232,6 @@ ${context.stackTrace}
|
|
|
196
232
|
\`\`\`typescript
|
|
197
233
|
${this.state.originalScript}
|
|
198
234
|
\`\`\`
|
|
199
|
-
|
|
200
|
-
${pageState}
|
|
201
|
-
}
|
|
202
235
|
`.trim();
|
|
203
236
|
}
|
|
204
237
|
|
|
@@ -237,7 +270,6 @@ ${pageState}
|
|
|
237
270
|
|
|
238
271
|
// If object, try to extract text field or convert to string
|
|
239
272
|
if (typeof content === 'object' && content !== null) {
|
|
240
|
-
``
|
|
241
273
|
if ('text' in content) {
|
|
242
274
|
return content as Record<string, unknown>;
|
|
243
275
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { BrowserContext } from 'playwright';
|
|
8
|
+
import type { Root } from '@modelcontextprotocol/sdk/types.js';
|
|
8
9
|
|
|
9
10
|
export interface BrowserContextFactory {
|
|
10
11
|
createContext: (
|
|
@@ -27,7 +28,7 @@ export interface BackendConfig {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export interface BrowserServerBackendInstance {
|
|
30
|
-
initialize(clientInfo: { name: string; version: string }): Promise<void>;
|
|
31
|
+
initialize(clientInfo: { name: string; version: string; roots: Root[]}): Promise<void>;
|
|
31
32
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
33
|
listTools(): Promise<Array<{ name: string; description: string; inputSchema: any }>>;
|
|
33
34
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
package/src/runner.ts
CHANGED
|
@@ -23,15 +23,25 @@ export class Runner {
|
|
|
23
23
|
private state: PlaywrightAgentState | undefined;
|
|
24
24
|
private script?: string;
|
|
25
25
|
private variables: Map<string, any> = new Map<string, any>();
|
|
26
|
+
private abortController: AbortController;
|
|
26
27
|
|
|
27
28
|
private constructor(config: RunnerConfig) {
|
|
28
29
|
this.config = config;
|
|
29
30
|
this.storageService = StorageService.getInstance(config.projectId);
|
|
30
31
|
this.logger = new Logger();
|
|
32
|
+
this.abortController = new AbortController();
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
static NewInstance(projectId: string, testCaseId: string): Runner {
|
|
35
|
+
static NewInstance(projectId: string, testCaseId: string, canHeal: boolean = true): Runner {
|
|
34
36
|
const runner = new Runner({ projectId, testCaseId });
|
|
37
|
+
runner.canHeal = canHeal;
|
|
38
|
+
if (!canHeal) {
|
|
39
|
+
return runner;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (process.env.NOARTIK_CAN_HEAL === 'true') {
|
|
43
|
+
runner.canHeal = true;
|
|
44
|
+
}
|
|
35
45
|
|
|
36
46
|
try {
|
|
37
47
|
const script = runner.loadOriginalScript();
|
|
@@ -57,6 +67,11 @@ export class Runner {
|
|
|
57
67
|
return runner;
|
|
58
68
|
}
|
|
59
69
|
|
|
70
|
+
stop() {
|
|
71
|
+
this.abortController.abort();
|
|
72
|
+
this.logger.log('Runner execution aborted.');
|
|
73
|
+
}
|
|
74
|
+
|
|
60
75
|
async init(page: Page, context: BrowserContext, data?: Record<string, any>): Promise<void> {
|
|
61
76
|
const adapter = await PlaywrightBackendAdapter.create(context);
|
|
62
77
|
this.state = {
|
|
@@ -76,7 +91,7 @@ export class Runner {
|
|
|
76
91
|
this.set(key, value);
|
|
77
92
|
}
|
|
78
93
|
}
|
|
79
|
-
this.executor = new PlaywrightExecutor(this.state);
|
|
94
|
+
this.executor = new PlaywrightExecutor(this.state, this.abortController.signal);
|
|
80
95
|
}
|
|
81
96
|
|
|
82
97
|
private loadOriginalScript(): string {
|
|
@@ -84,7 +99,7 @@ export class Runner {
|
|
|
84
99
|
throw new Error('Cannot load original script: testCaseId is not provided');
|
|
85
100
|
}
|
|
86
101
|
|
|
87
|
-
this.logger.log(`Attempting to load script from database: ${this.config.
|
|
102
|
+
this.logger.log(`Attempting to load script from database: ${this.config.testCaseId}`);
|
|
88
103
|
const script = this.storageService.loadTestScript(this.config.testCaseId);
|
|
89
104
|
if (!script) {
|
|
90
105
|
throw new Error(`Cannot load original script: Test case not found in database (ID: ${this.config.testCaseId})`);
|
|
@@ -93,16 +108,6 @@ export class Runner {
|
|
|
93
108
|
return script;
|
|
94
109
|
}
|
|
95
110
|
|
|
96
|
-
forEach(data: Record<string, any>[], callback: (index: number) => void) {
|
|
97
|
-
for (const [index, item] of data.entries()) {
|
|
98
|
-
for (const [key, value] of Object.entries(item)) {
|
|
99
|
-
this.set(key, value);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
callback(index);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
111
|
async runStep(description: string, fn: () => Promise<void>) {
|
|
107
112
|
const startTime = Date.now();
|
|
108
113
|
const context: HealContext = {
|
|
@@ -126,7 +131,7 @@ export class Runner {
|
|
|
126
131
|
}
|
|
127
132
|
|
|
128
133
|
context.error = (error as Error).message;
|
|
129
|
-
context.stackTrace = (error as Error).stack;
|
|
134
|
+
context.stackTrace = this.removeAnsiCodes((error as Error).stack || '');
|
|
130
135
|
|
|
131
136
|
this.logger.error(`Step failed: ${description}, Error: ${context.error}`);
|
|
132
137
|
this.logger.log(`Agent will attempt to self-heal...`);
|
|
@@ -144,6 +149,14 @@ export class Runner {
|
|
|
144
149
|
}
|
|
145
150
|
}
|
|
146
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Remove ANSI color codes from string
|
|
154
|
+
*/
|
|
155
|
+
private removeAnsiCodes(str: string): string {
|
|
156
|
+
// Remove ANSI escape sequences (e.g., [31m, [39m, \x1b[31m)
|
|
157
|
+
return str.replace(/\x1b\[[0-9;]*m|\u001b\[[0-9;]*m|\[3[0-9]m|\[39m|\[0m/g, '');
|
|
158
|
+
}
|
|
159
|
+
|
|
147
160
|
private isHealableError(err: Error): boolean {
|
|
148
161
|
// 方式1:枚举所有 Symbol 属性(最常见尝试)
|
|
149
162
|
const symbols = Object.getOwnPropertySymbols(err);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
3
|
+
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
timeout: 600000,
|
|
6
|
+
reporter: 'json',
|
|
7
|
+
use: {
|
|
8
|
+
actionTimeout: 15000,
|
|
9
|
+
navigationTimeout: 60000,
|
|
10
|
+
locale: 'zh-CN',
|
|
11
|
+
},
|
|
12
|
+
expect: {
|
|
13
|
+
timeout: 5000,
|
|
14
|
+
},
|
|
15
|
+
/* Configure projects for major browsers */
|
|
16
|
+
projects: [
|
|
17
|
+
{
|
|
18
|
+
name: 'chromium',
|
|
19
|
+
use: { ...devices['Desktop Chrome'] },
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
name: 'firefox',
|
|
24
|
+
use: { ...devices['Desktop Firefox'] },
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
{
|
|
28
|
+
name: 'webkit',
|
|
29
|
+
use: { ...devices['Desktop Safari'] },
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
});
|
|
@@ -1,54 +1,42 @@
|
|
|
1
1
|
|
|
2
2
|
import { test, expect } from '@playwright/test';
|
|
3
|
-
import { Runner } from '
|
|
3
|
+
import { Runner } from '../../src/runner';
|
|
4
|
+
test.describe(`Login user`, () => {
|
|
5
|
+
test.setTimeout(1000000);
|
|
6
|
+
const runner = Runner.NewInstance('43af4db4-3249-4135-b279-97b24f27106b', '13bcfb12-4add-4b7d-ac4b-b46a4280141c');
|
|
4
7
|
|
|
5
|
-
test.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
test.afterEach(async () => {
|
|
9
|
-
runner.clear();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
[
|
|
13
|
-
{
|
|
14
|
-
"password": "abcd1234"
|
|
15
|
-
}
|
|
16
|
-
].forEach((data, index) => {
|
|
17
|
-
test(`#${index + 1}`, async ({ page, context }) => {
|
|
18
|
-
await runner.init(page, context, data);
|
|
19
|
-
|
|
20
|
-
await runner.runStep('进入注册页面', async () => {
|
|
21
|
-
await page.goto('https://practice.expandtesting.com/register');
|
|
22
|
-
});
|
|
8
|
+
test.afterEach(async () => {
|
|
9
|
+
runner.clear();
|
|
10
|
+
});
|
|
23
11
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
username = runner.get('username');
|
|
29
|
-
} else {
|
|
30
|
-
const timestamp = Date.now();
|
|
31
|
-
username = `user${timestamp}`;
|
|
12
|
+
[
|
|
13
|
+
{
|
|
14
|
+
"username": "practice",
|
|
15
|
+
"password": "SuperSecretPassword!"
|
|
32
16
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
17
|
+
].forEach((data, index) => {
|
|
18
|
+
test(`#${index + 1}`, async ({ page, context }) => {
|
|
19
|
+
await runner.init(page, context, data);
|
|
20
|
+
|
|
21
|
+
// FIXED
|
|
22
|
+
await runner.runStep('进入登录页面', async () => {
|
|
23
|
+
await page.goto('https://parabank.parasoft.com/parabank/index.htm');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
await runner.runStep('输入有效凭据并提交登录表单', async () => {
|
|
27
|
+
const username = runner.get('username');
|
|
28
|
+
const password = runner.get('password');
|
|
29
|
+
|
|
30
|
+
// 填写用户名
|
|
31
|
+
await page.locator('input[name="User_name"]').fill(username, { timeout: 3000 });
|
|
32
|
+
// 填写密码
|
|
33
|
+
await page.locator('input[name="password"]').fill(password);
|
|
34
|
+
// 点击登录按钮
|
|
35
|
+
await page.getByRole('button', { name: 'Log In' }).click();
|
|
36
|
+
|
|
37
|
+
// 验证登录成功(URL变化)
|
|
38
|
+
await expect(page).toHaveURL(/overview.htm/);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
52
41
|
});
|
|
53
|
-
});
|
|
54
42
|
});
|