@midscene/shared 0.18.1-beta-20250611082446.0 → 0.18.1
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/dist/es/constants.d.ts +4 -1
- package/dist/es/constants.js +104 -1
- package/dist/es/extractor-debug.js +0 -40
- package/dist/es/extractor.d.ts +1 -1
- package/dist/es/extractor.js +0 -40
- package/dist/es/img.d.ts +1 -1
- package/dist/es/{index-a171e36c.d.ts → index-59587d83.d.ts} +0 -2
- package/dist/es/types.d.ts +1 -1
- package/dist/lib/constants.d.ts +4 -1
- package/dist/lib/constants.js +109 -2
- package/dist/lib/extractor-debug.js +0 -40
- package/dist/lib/extractor.d.ts +1 -1
- package/dist/lib/extractor.js +0 -40
- package/dist/lib/img.d.ts +1 -1
- package/dist/lib/{index-a171e36c.d.ts → index-59587d83.d.ts} +0 -2
- package/dist/lib/types.d.ts +1 -1
- package/dist/script/htmlElement.js +0 -40
- package/dist/script/htmlElementDebug.js +0 -40
- package/dist/types/constants.d.ts +4 -1
- package/dist/types/extractor.d.ts +1 -1
- package/dist/types/img.d.ts +1 -1
- package/dist/types/{index-a171e36c.d.ts → index-59587d83.d.ts} +0 -2
- package/dist/types/types.d.ts +1 -1
- package/package.json +3 -2
- package/src/constants/example-code.ts +100 -0
- package/src/constants/index.ts +2 -0
- package/src/extractor/index.ts +0 -1
- package/src/extractor/util.ts +0 -38
- package/src/extractor/web-extractor.ts +0 -13
- package/src/types/index.ts +0 -2
package/dist/es/constants.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
declare const PLAYWRIGHT_EXAMPLE_CODE = "\n// Reference the following code to generate Midscene test cases\n// The following is test code for Midscene AI, for reference\n// The following is Playwright syntax, you can use Playwright to assist in test generation\nIMPORTANT: Follow these exact type signatures for AI functions:\n\n// Type signatures for AI functions:\naiInput(value: string, locator: string): Promise<void>\naiTap(locator: string): Promise<void> \naiAssert(assertion: string): Promise<void>\naiQuery<T>(queryObject: Record<string, string>): Promise<T> // Extracts data from page based on descriptions\n\n// examples:\n// Reference the following code to generate Midscene test cases\n// The following is test code for Midscene AI, for reference\n// The following is Playwright syntax, you can use Playwright to assist in test generation\nimport { test as base } from '@playwright/test';\nimport type { PlayWrightAiFixtureType } from '@midscene/web/playwright';\nimport { PlaywrightAiFixture } from '@midscene/web/playwright';\n\nexport const test = base.extend<PlayWrightAiFixtureType>(PlaywrightAiFixture({\n waitForNetworkIdleTimeout: 2000, // optional, the timeout for waiting for network idle between each action, default is 2000ms\n }));\n\n\ntest.beforeEach(async ({ page }) => {\n await page.goto('https://www.xxx.com/');\n await page.setViewportSize({ width: 1920, height: 1080 });\n});\n\ntest('ai shop', async ({\n aiInput,\n aiAssert,\n aiQuery,\n aiKeyboardPress,\n aiHover,\n aiTap,\n agentForPage,\n page,\n}) => {\n // login\n await aiAssert('The page shows the login interface');\n await aiInput('user_name', 'in user name input');\n await aiInput('password', 'in password input');\n await aiKeyboardPress('Enter', 'Login Button');\n\n // check the login success\n await aiWaitFor('The page shows that the loading is complete');\n await aiAssert('The current page shows the product detail page');\n\n // check the product info\n const dataA = await aiQuery({\n userInfo: 'User information in the format {name: string}',\n theFirstProductInfo: 'The first product info in the format {name: string, price: number}',\n });\n expect(dataA.theFirstProductInfo.name).toBe('xxx');\n expect(dataA.theFirstProductInfo.price).toBe(100);\n\n\n // add to cart\n await aiTap('click add to cart button');\n \n await aiTap('click right top cart icon');\n await aiAssert('The cart icon shows the number 1');\n});\n";
|
|
2
|
+
declare const YAML_EXAMPLE_CODE = "\n1. Format:\n\ntarget:\n url: \"starting_url\"\n viewportWidth: 1280\n viewportHeight: 960\n\ntasks:\n - name: \"descriptive task name\"\n flow:\n - aiTap: \"element description\"\n - aiInput: 'text value'\n locate: 'input field description'\n - aiScroll:\n direction: down/up\n scrollType: untilBottom/untilTop/page\n - aiAssert: \"expected state\"\n - sleep: milliseconds\n\n2. Action Types:\n- aiTap: for clicks (natural language targeting)\n- aiInput: for text input with 'locate' field\n- aiScroll: with direction and scrollType\n- aiAssert: for validations\n- sleep: for delays (milliseconds)\n\n3. Best Practices:\n- Group related actions into logical tasks\n- Use natural language descriptions\n- Add deepThink: true for complex interactions\n- Keep task names concise but descriptive\n";
|
|
3
|
+
|
|
1
4
|
declare const TEXT_SIZE_THRESHOLD = 9;
|
|
2
5
|
declare const TEXT_MAX_SIZE = 40;
|
|
3
6
|
declare const CONTAINER_MINI_HEIGHT = 3;
|
|
@@ -18,4 +21,4 @@ declare const DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT = 2000;
|
|
|
18
21
|
declare const DEFAULT_WAIT_FOR_NETWORK_IDLE_TIME = 300;
|
|
19
22
|
declare const DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY = 2;
|
|
20
23
|
|
|
21
|
-
export { CONTAINER_MINI_HEIGHT, CONTAINER_MINI_WIDTH, DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY, DEFAULT_WAIT_FOR_NETWORK_IDLE_TIME, DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT, NodeType, PLAYGROUND_SERVER_PORT, SCRCPY_SERVER_PORT, TEXT_MAX_SIZE, TEXT_SIZE_THRESHOLD };
|
|
24
|
+
export { CONTAINER_MINI_HEIGHT, CONTAINER_MINI_WIDTH, DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY, DEFAULT_WAIT_FOR_NETWORK_IDLE_TIME, DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT, NodeType, PLAYGROUND_SERVER_PORT, PLAYWRIGHT_EXAMPLE_CODE, SCRCPY_SERVER_PORT, TEXT_MAX_SIZE, TEXT_SIZE_THRESHOLD, YAML_EXAMPLE_CODE };
|
package/dist/es/constants.js
CHANGED
|
@@ -1,3 +1,104 @@
|
|
|
1
|
+
// src/constants/example-code.ts
|
|
2
|
+
var PLAYWRIGHT_EXAMPLE_CODE = `
|
|
3
|
+
// Reference the following code to generate Midscene test cases
|
|
4
|
+
// The following is test code for Midscene AI, for reference
|
|
5
|
+
// The following is Playwright syntax, you can use Playwright to assist in test generation
|
|
6
|
+
IMPORTANT: Follow these exact type signatures for AI functions:
|
|
7
|
+
|
|
8
|
+
// Type signatures for AI functions:
|
|
9
|
+
aiInput(value: string, locator: string): Promise<void>
|
|
10
|
+
aiTap(locator: string): Promise<void>
|
|
11
|
+
aiAssert(assertion: string): Promise<void>
|
|
12
|
+
aiQuery<T>(queryObject: Record<string, string>): Promise<T> // Extracts data from page based on descriptions
|
|
13
|
+
|
|
14
|
+
// examples:
|
|
15
|
+
// Reference the following code to generate Midscene test cases
|
|
16
|
+
// The following is test code for Midscene AI, for reference
|
|
17
|
+
// The following is Playwright syntax, you can use Playwright to assist in test generation
|
|
18
|
+
import { test as base } from '@playwright/test';
|
|
19
|
+
import type { PlayWrightAiFixtureType } from '@midscene/web/playwright';
|
|
20
|
+
import { PlaywrightAiFixture } from '@midscene/web/playwright';
|
|
21
|
+
|
|
22
|
+
export const test = base.extend<PlayWrightAiFixtureType>(PlaywrightAiFixture({
|
|
23
|
+
waitForNetworkIdleTimeout: 2000, // optional, the timeout for waiting for network idle between each action, default is 2000ms
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
test.beforeEach(async ({ page }) => {
|
|
28
|
+
await page.goto('https://www.xxx.com/');
|
|
29
|
+
await page.setViewportSize({ width: 1920, height: 1080 });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('ai shop', async ({
|
|
33
|
+
aiInput,
|
|
34
|
+
aiAssert,
|
|
35
|
+
aiQuery,
|
|
36
|
+
aiKeyboardPress,
|
|
37
|
+
aiHover,
|
|
38
|
+
aiTap,
|
|
39
|
+
agentForPage,
|
|
40
|
+
page,
|
|
41
|
+
}) => {
|
|
42
|
+
// login
|
|
43
|
+
await aiAssert('The page shows the login interface');
|
|
44
|
+
await aiInput('user_name', 'in user name input');
|
|
45
|
+
await aiInput('password', 'in password input');
|
|
46
|
+
await aiKeyboardPress('Enter', 'Login Button');
|
|
47
|
+
|
|
48
|
+
// check the login success
|
|
49
|
+
await aiWaitFor('The page shows that the loading is complete');
|
|
50
|
+
await aiAssert('The current page shows the product detail page');
|
|
51
|
+
|
|
52
|
+
// check the product info
|
|
53
|
+
const dataA = await aiQuery({
|
|
54
|
+
userInfo: 'User information in the format {name: string}',
|
|
55
|
+
theFirstProductInfo: 'The first product info in the format {name: string, price: number}',
|
|
56
|
+
});
|
|
57
|
+
expect(dataA.theFirstProductInfo.name).toBe('xxx');
|
|
58
|
+
expect(dataA.theFirstProductInfo.price).toBe(100);
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
// add to cart
|
|
62
|
+
await aiTap('click add to cart button');
|
|
63
|
+
|
|
64
|
+
await aiTap('click right top cart icon');
|
|
65
|
+
await aiAssert('The cart icon shows the number 1');
|
|
66
|
+
});
|
|
67
|
+
`;
|
|
68
|
+
var YAML_EXAMPLE_CODE = `
|
|
69
|
+
1. Format:
|
|
70
|
+
|
|
71
|
+
target:
|
|
72
|
+
url: "starting_url"
|
|
73
|
+
viewportWidth: 1280
|
|
74
|
+
viewportHeight: 960
|
|
75
|
+
|
|
76
|
+
tasks:
|
|
77
|
+
- name: "descriptive task name"
|
|
78
|
+
flow:
|
|
79
|
+
- aiTap: "element description"
|
|
80
|
+
- aiInput: 'text value'
|
|
81
|
+
locate: 'input field description'
|
|
82
|
+
- aiScroll:
|
|
83
|
+
direction: down/up
|
|
84
|
+
scrollType: untilBottom/untilTop/page
|
|
85
|
+
- aiAssert: "expected state"
|
|
86
|
+
- sleep: milliseconds
|
|
87
|
+
|
|
88
|
+
2. Action Types:
|
|
89
|
+
- aiTap: for clicks (natural language targeting)
|
|
90
|
+
- aiInput: for text input with 'locate' field
|
|
91
|
+
- aiScroll: with direction and scrollType
|
|
92
|
+
- aiAssert: for validations
|
|
93
|
+
- sleep: for delays (milliseconds)
|
|
94
|
+
|
|
95
|
+
3. Best Practices:
|
|
96
|
+
- Group related actions into logical tasks
|
|
97
|
+
- Use natural language descriptions
|
|
98
|
+
- Add deepThink: true for complex interactions
|
|
99
|
+
- Keep task names concise but descriptive
|
|
100
|
+
`;
|
|
101
|
+
|
|
1
102
|
// src/constants/index.ts
|
|
2
103
|
var TEXT_SIZE_THRESHOLD = 9;
|
|
3
104
|
var TEXT_MAX_SIZE = 40;
|
|
@@ -28,7 +129,9 @@ export {
|
|
|
28
129
|
DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,
|
|
29
130
|
NodeType,
|
|
30
131
|
PLAYGROUND_SERVER_PORT,
|
|
132
|
+
PLAYWRIGHT_EXAMPLE_CODE,
|
|
31
133
|
SCRCPY_SERVER_PORT,
|
|
32
134
|
TEXT_MAX_SIZE,
|
|
33
|
-
TEXT_SIZE_THRESHOLD
|
|
135
|
+
TEXT_SIZE_THRESHOLD,
|
|
136
|
+
YAML_EXAMPLE_CODE
|
|
34
137
|
};
|
|
@@ -110,40 +110,12 @@ var debugMode = false;
|
|
|
110
110
|
function setDebugMode(mode) {
|
|
111
111
|
debugMode = mode;
|
|
112
112
|
}
|
|
113
|
-
function getDebugMode() {
|
|
114
|
-
return debugMode;
|
|
115
|
-
}
|
|
116
113
|
function logger(..._msg) {
|
|
117
114
|
if (!debugMode) {
|
|
118
115
|
return;
|
|
119
116
|
}
|
|
120
117
|
console.log(..._msg);
|
|
121
118
|
}
|
|
122
|
-
var taskIdKey = "_midscene_retrieve_task_id";
|
|
123
|
-
function selectorForValue(val) {
|
|
124
|
-
return `[${taskIdKey}='${val}']`;
|
|
125
|
-
}
|
|
126
|
-
function setDataForNode(node, nodeHash, setToParentNode, currentWindow) {
|
|
127
|
-
const taskId = taskIdKey;
|
|
128
|
-
if (!(node instanceof currentWindow.HTMLElement)) {
|
|
129
|
-
return "";
|
|
130
|
-
}
|
|
131
|
-
if (!taskId) {
|
|
132
|
-
console.error("No task id found");
|
|
133
|
-
return "";
|
|
134
|
-
}
|
|
135
|
-
const selector = selectorForValue(nodeHash);
|
|
136
|
-
if (getDebugMode()) {
|
|
137
|
-
if (setToParentNode) {
|
|
138
|
-
if (node.parentNode instanceof currentWindow.HTMLElement) {
|
|
139
|
-
node.parentNode.setAttribute(taskIdKey, nodeHash.toString());
|
|
140
|
-
}
|
|
141
|
-
} else {
|
|
142
|
-
node.setAttribute(taskIdKey, nodeHash.toString());
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return selector;
|
|
146
|
-
}
|
|
147
119
|
function isElementPartiallyInViewport(rect, currentWindow, currentDocument, visibleAreaRatio = 2 / 3) {
|
|
148
120
|
const elementHeight = rect.height;
|
|
149
121
|
const elementWidth = rect.width;
|
|
@@ -430,7 +402,6 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
430
402
|
const attributes = getNodeAttributes(node, currentWindow);
|
|
431
403
|
let valueContent = attributes.value || attributes.placeholder || node.textContent || "";
|
|
432
404
|
const nodeHashId = midsceneGenerateHash(node, valueContent, rect);
|
|
433
|
-
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
434
405
|
const tagName = node.tagName.toLowerCase();
|
|
435
406
|
if (node.tagName.toLowerCase() === "select") {
|
|
436
407
|
const selectedOption = node.options[node.selectedIndex];
|
|
@@ -442,7 +413,6 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
442
413
|
const elementInfo = {
|
|
443
414
|
id: nodeHashId,
|
|
444
415
|
nodeHashId,
|
|
445
|
-
locator: selector,
|
|
446
416
|
nodeType: "FORM_ITEM Node" /* FORM_ITEM */,
|
|
447
417
|
indexId: indexId++,
|
|
448
418
|
attributes: {
|
|
@@ -475,13 +445,11 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
475
445
|
const pseudo = getPseudoElementContent(node, currentWindow);
|
|
476
446
|
const content = node.innerText || pseudo.before || pseudo.after || "";
|
|
477
447
|
const nodeHashId = midsceneGenerateHash(node, content, rect2);
|
|
478
|
-
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
479
448
|
const elementInfo = {
|
|
480
449
|
id: nodeHashId,
|
|
481
450
|
indexId: indexId++,
|
|
482
451
|
nodeHashId,
|
|
483
452
|
nodeType: "BUTTON Node" /* BUTTON */,
|
|
484
|
-
locator: selector,
|
|
485
453
|
attributes: {
|
|
486
454
|
...attributes,
|
|
487
455
|
htmlTagName: tagNameOfNode(node),
|
|
@@ -501,12 +469,10 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
501
469
|
if (isImgElement(node)) {
|
|
502
470
|
const attributes = getNodeAttributes(node, currentWindow);
|
|
503
471
|
const nodeHashId = midsceneGenerateHash(node, "", rect);
|
|
504
|
-
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
505
472
|
const elementInfo = {
|
|
506
473
|
id: nodeHashId,
|
|
507
474
|
indexId: indexId++,
|
|
508
475
|
nodeHashId,
|
|
509
|
-
locator: selector,
|
|
510
476
|
attributes: {
|
|
511
477
|
...attributes,
|
|
512
478
|
...node.nodeName.toLowerCase() === "svg" ? {
|
|
@@ -538,13 +504,11 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
538
504
|
return null;
|
|
539
505
|
}
|
|
540
506
|
const nodeHashId = midsceneGenerateHash(node, text, rect);
|
|
541
|
-
const selector = setDataForNode(node, nodeHashId, true, currentWindow);
|
|
542
507
|
const elementInfo = {
|
|
543
508
|
id: nodeHashId,
|
|
544
509
|
indexId: indexId++,
|
|
545
510
|
nodeHashId,
|
|
546
511
|
nodeType: "TEXT Node" /* TEXT */,
|
|
547
|
-
locator: selector,
|
|
548
512
|
attributes: {
|
|
549
513
|
...attributes,
|
|
550
514
|
nodeType: "TEXT Node" /* TEXT */,
|
|
@@ -566,13 +530,11 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
566
530
|
const pseudo = getPseudoElementContent(node, currentWindow);
|
|
567
531
|
const content = node.innerText || pseudo.before || pseudo.after || "";
|
|
568
532
|
const nodeHashId = midsceneGenerateHash(node, content, rect);
|
|
569
|
-
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
570
533
|
const elementInfo = {
|
|
571
534
|
id: nodeHashId,
|
|
572
535
|
indexId: indexId++,
|
|
573
536
|
nodeHashId,
|
|
574
537
|
nodeType: "Anchor Node" /* A */,
|
|
575
|
-
locator: selector,
|
|
576
538
|
attributes: {
|
|
577
539
|
...attributes,
|
|
578
540
|
htmlTagName: tagNameOfNode(node),
|
|
@@ -592,13 +554,11 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
592
554
|
if (isContainerElement(node)) {
|
|
593
555
|
const attributes = getNodeAttributes(node, currentWindow);
|
|
594
556
|
const nodeHashId = midsceneGenerateHash(node, "", rect);
|
|
595
|
-
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
596
557
|
const elementInfo = {
|
|
597
558
|
id: nodeHashId,
|
|
598
559
|
nodeHashId,
|
|
599
560
|
indexId: indexId++,
|
|
600
561
|
nodeType: "CONTAINER Node" /* CONTAINER */,
|
|
601
|
-
locator: selector,
|
|
602
562
|
attributes: {
|
|
603
563
|
...attributes,
|
|
604
564
|
nodeType: "CONTAINER Node" /* CONTAINER */,
|
package/dist/es/extractor.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import './constants.js';
|
|
2
|
-
export { E as ElementInfo, a as ElementNode, d as descriptionOfTree, m as generateElementByPosition, l as getElementInfoByXpath, i as getNodeFromCacheList, k as getNodeInfoByXpath, j as getXpathsById, s as setNodeHashCacheListOnWindow, t as traverseTree, b as treeToList, e as trimAttributes, c as truncateText, g as webExtractNodeTree, h as webExtractNodeTreeAsString, f as webExtractTextWithPosition } from './index-
|
|
2
|
+
export { E as ElementInfo, a as ElementNode, d as descriptionOfTree, m as generateElementByPosition, l as getElementInfoByXpath, i as getNodeFromCacheList, k as getNodeInfoByXpath, j as getXpathsById, s as setNodeHashCacheListOnWindow, t as traverseTree, b as treeToList, e as trimAttributes, c as truncateText, g as webExtractNodeTree, h as webExtractNodeTreeAsString, f as webExtractTextWithPosition } from './index-59587d83.js';
|
package/dist/es/extractor.js
CHANGED
|
@@ -252,40 +252,12 @@ var debugMode = false;
|
|
|
252
252
|
function setDebugMode(mode) {
|
|
253
253
|
debugMode = mode;
|
|
254
254
|
}
|
|
255
|
-
function getDebugMode() {
|
|
256
|
-
return debugMode;
|
|
257
|
-
}
|
|
258
255
|
function logger(..._msg) {
|
|
259
256
|
if (!debugMode) {
|
|
260
257
|
return;
|
|
261
258
|
}
|
|
262
259
|
console.log(..._msg);
|
|
263
260
|
}
|
|
264
|
-
var taskIdKey = "_midscene_retrieve_task_id";
|
|
265
|
-
function selectorForValue(val) {
|
|
266
|
-
return `[${taskIdKey}='${val}']`;
|
|
267
|
-
}
|
|
268
|
-
function setDataForNode(node, nodeHash, setToParentNode, currentWindow) {
|
|
269
|
-
const taskId = taskIdKey;
|
|
270
|
-
if (!(node instanceof currentWindow.HTMLElement)) {
|
|
271
|
-
return "";
|
|
272
|
-
}
|
|
273
|
-
if (!taskId) {
|
|
274
|
-
console.error("No task id found");
|
|
275
|
-
return "";
|
|
276
|
-
}
|
|
277
|
-
const selector = selectorForValue(nodeHash);
|
|
278
|
-
if (getDebugMode()) {
|
|
279
|
-
if (setToParentNode) {
|
|
280
|
-
if (node.parentNode instanceof currentWindow.HTMLElement) {
|
|
281
|
-
node.parentNode.setAttribute(taskIdKey, nodeHash.toString());
|
|
282
|
-
}
|
|
283
|
-
} else {
|
|
284
|
-
node.setAttribute(taskIdKey, nodeHash.toString());
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
return selector;
|
|
288
|
-
}
|
|
289
261
|
function isElementPartiallyInViewport(rect, currentWindow, currentDocument, visibleAreaRatio = 2 / 3) {
|
|
290
262
|
const elementHeight = rect.height;
|
|
291
263
|
const elementWidth = rect.width;
|
|
@@ -562,7 +534,6 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
562
534
|
const attributes = getNodeAttributes(node, currentWindow);
|
|
563
535
|
let valueContent = attributes.value || attributes.placeholder || node.textContent || "";
|
|
564
536
|
const nodeHashId = midsceneGenerateHash(node, valueContent, rect);
|
|
565
|
-
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
566
537
|
const tagName = node.tagName.toLowerCase();
|
|
567
538
|
if (node.tagName.toLowerCase() === "select") {
|
|
568
539
|
const selectedOption = node.options[node.selectedIndex];
|
|
@@ -574,7 +545,6 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
574
545
|
const elementInfo = {
|
|
575
546
|
id: nodeHashId,
|
|
576
547
|
nodeHashId,
|
|
577
|
-
locator: selector,
|
|
578
548
|
nodeType: "FORM_ITEM Node" /* FORM_ITEM */,
|
|
579
549
|
indexId: indexId++,
|
|
580
550
|
attributes: {
|
|
@@ -607,13 +577,11 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
607
577
|
const pseudo = getPseudoElementContent(node, currentWindow);
|
|
608
578
|
const content = node.innerText || pseudo.before || pseudo.after || "";
|
|
609
579
|
const nodeHashId = midsceneGenerateHash(node, content, rect2);
|
|
610
|
-
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
611
580
|
const elementInfo = {
|
|
612
581
|
id: nodeHashId,
|
|
613
582
|
indexId: indexId++,
|
|
614
583
|
nodeHashId,
|
|
615
584
|
nodeType: "BUTTON Node" /* BUTTON */,
|
|
616
|
-
locator: selector,
|
|
617
585
|
attributes: {
|
|
618
586
|
...attributes,
|
|
619
587
|
htmlTagName: tagNameOfNode(node),
|
|
@@ -633,12 +601,10 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
633
601
|
if (isImgElement(node)) {
|
|
634
602
|
const attributes = getNodeAttributes(node, currentWindow);
|
|
635
603
|
const nodeHashId = midsceneGenerateHash(node, "", rect);
|
|
636
|
-
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
637
604
|
const elementInfo = {
|
|
638
605
|
id: nodeHashId,
|
|
639
606
|
indexId: indexId++,
|
|
640
607
|
nodeHashId,
|
|
641
|
-
locator: selector,
|
|
642
608
|
attributes: {
|
|
643
609
|
...attributes,
|
|
644
610
|
...node.nodeName.toLowerCase() === "svg" ? {
|
|
@@ -670,13 +636,11 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
670
636
|
return null;
|
|
671
637
|
}
|
|
672
638
|
const nodeHashId = midsceneGenerateHash(node, text, rect);
|
|
673
|
-
const selector = setDataForNode(node, nodeHashId, true, currentWindow);
|
|
674
639
|
const elementInfo = {
|
|
675
640
|
id: nodeHashId,
|
|
676
641
|
indexId: indexId++,
|
|
677
642
|
nodeHashId,
|
|
678
643
|
nodeType: "TEXT Node" /* TEXT */,
|
|
679
|
-
locator: selector,
|
|
680
644
|
attributes: {
|
|
681
645
|
...attributes,
|
|
682
646
|
nodeType: "TEXT Node" /* TEXT */,
|
|
@@ -698,13 +662,11 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
698
662
|
const pseudo = getPseudoElementContent(node, currentWindow);
|
|
699
663
|
const content = node.innerText || pseudo.before || pseudo.after || "";
|
|
700
664
|
const nodeHashId = midsceneGenerateHash(node, content, rect);
|
|
701
|
-
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
702
665
|
const elementInfo = {
|
|
703
666
|
id: nodeHashId,
|
|
704
667
|
indexId: indexId++,
|
|
705
668
|
nodeHashId,
|
|
706
669
|
nodeType: "Anchor Node" /* A */,
|
|
707
|
-
locator: selector,
|
|
708
670
|
attributes: {
|
|
709
671
|
...attributes,
|
|
710
672
|
htmlTagName: tagNameOfNode(node),
|
|
@@ -724,13 +686,11 @@ function collectElementInfo(node, currentWindow, currentDocument, baseZoom = 1,
|
|
|
724
686
|
if (isContainerElement(node)) {
|
|
725
687
|
const attributes = getNodeAttributes(node, currentWindow);
|
|
726
688
|
const nodeHashId = midsceneGenerateHash(node, "", rect);
|
|
727
|
-
const selector = setDataForNode(node, nodeHashId, false, currentWindow);
|
|
728
689
|
const elementInfo = {
|
|
729
690
|
id: nodeHashId,
|
|
730
691
|
nodeHashId,
|
|
731
692
|
indexId: indexId++,
|
|
732
693
|
nodeType: "CONTAINER Node" /* CONTAINER */,
|
|
733
|
-
locator: selector,
|
|
734
694
|
attributes: {
|
|
735
695
|
...attributes,
|
|
736
696
|
nodeType: "CONTAINER Node" /* CONTAINER */,
|
package/dist/es/img.d.ts
CHANGED
|
@@ -46,7 +46,6 @@ interface ElementInfo {
|
|
|
46
46
|
id: string;
|
|
47
47
|
indexId: number;
|
|
48
48
|
nodeHashId: string;
|
|
49
|
-
locator: string;
|
|
50
49
|
xpaths?: string[];
|
|
51
50
|
attributes: {
|
|
52
51
|
nodeType: NodeType;
|
|
@@ -90,7 +89,6 @@ declare abstract class BaseElement {
|
|
|
90
89
|
abstract content: string;
|
|
91
90
|
abstract rect: Rect;
|
|
92
91
|
abstract center: [number, number];
|
|
93
|
-
abstract locator?: string;
|
|
94
92
|
abstract xpaths?: string[];
|
|
95
93
|
abstract isVisible: boolean;
|
|
96
94
|
}
|
package/dist/es/types.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import './constants.js';
|
|
2
|
-
export { B as BaseElement, n as ElementTreeNode, P as Point, R as Rect, S as Size, W as WebElementInfo } from './index-
|
|
2
|
+
export { B as BaseElement, n as ElementTreeNode, P as Point, R as Rect, S as Size, W as WebElementInfo } from './index-59587d83.js';
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
declare const PLAYWRIGHT_EXAMPLE_CODE = "\n// Reference the following code to generate Midscene test cases\n// The following is test code for Midscene AI, for reference\n// The following is Playwright syntax, you can use Playwright to assist in test generation\nIMPORTANT: Follow these exact type signatures for AI functions:\n\n// Type signatures for AI functions:\naiInput(value: string, locator: string): Promise<void>\naiTap(locator: string): Promise<void> \naiAssert(assertion: string): Promise<void>\naiQuery<T>(queryObject: Record<string, string>): Promise<T> // Extracts data from page based on descriptions\n\n// examples:\n// Reference the following code to generate Midscene test cases\n// The following is test code for Midscene AI, for reference\n// The following is Playwright syntax, you can use Playwright to assist in test generation\nimport { test as base } from '@playwright/test';\nimport type { PlayWrightAiFixtureType } from '@midscene/web/playwright';\nimport { PlaywrightAiFixture } from '@midscene/web/playwright';\n\nexport const test = base.extend<PlayWrightAiFixtureType>(PlaywrightAiFixture({\n waitForNetworkIdleTimeout: 2000, // optional, the timeout for waiting for network idle between each action, default is 2000ms\n }));\n\n\ntest.beforeEach(async ({ page }) => {\n await page.goto('https://www.xxx.com/');\n await page.setViewportSize({ width: 1920, height: 1080 });\n});\n\ntest('ai shop', async ({\n aiInput,\n aiAssert,\n aiQuery,\n aiKeyboardPress,\n aiHover,\n aiTap,\n agentForPage,\n page,\n}) => {\n // login\n await aiAssert('The page shows the login interface');\n await aiInput('user_name', 'in user name input');\n await aiInput('password', 'in password input');\n await aiKeyboardPress('Enter', 'Login Button');\n\n // check the login success\n await aiWaitFor('The page shows that the loading is complete');\n await aiAssert('The current page shows the product detail page');\n\n // check the product info\n const dataA = await aiQuery({\n userInfo: 'User information in the format {name: string}',\n theFirstProductInfo: 'The first product info in the format {name: string, price: number}',\n });\n expect(dataA.theFirstProductInfo.name).toBe('xxx');\n expect(dataA.theFirstProductInfo.price).toBe(100);\n\n\n // add to cart\n await aiTap('click add to cart button');\n \n await aiTap('click right top cart icon');\n await aiAssert('The cart icon shows the number 1');\n});\n";
|
|
2
|
+
declare const YAML_EXAMPLE_CODE = "\n1. Format:\n\ntarget:\n url: \"starting_url\"\n viewportWidth: 1280\n viewportHeight: 960\n\ntasks:\n - name: \"descriptive task name\"\n flow:\n - aiTap: \"element description\"\n - aiInput: 'text value'\n locate: 'input field description'\n - aiScroll:\n direction: down/up\n scrollType: untilBottom/untilTop/page\n - aiAssert: \"expected state\"\n - sleep: milliseconds\n\n2. Action Types:\n- aiTap: for clicks (natural language targeting)\n- aiInput: for text input with 'locate' field\n- aiScroll: with direction and scrollType\n- aiAssert: for validations\n- sleep: for delays (milliseconds)\n\n3. Best Practices:\n- Group related actions into logical tasks\n- Use natural language descriptions\n- Add deepThink: true for complex interactions\n- Keep task names concise but descriptive\n";
|
|
3
|
+
|
|
1
4
|
declare const TEXT_SIZE_THRESHOLD = 9;
|
|
2
5
|
declare const TEXT_MAX_SIZE = 40;
|
|
3
6
|
declare const CONTAINER_MINI_HEIGHT = 3;
|
|
@@ -18,4 +21,4 @@ declare const DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT = 2000;
|
|
|
18
21
|
declare const DEFAULT_WAIT_FOR_NETWORK_IDLE_TIME = 300;
|
|
19
22
|
declare const DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY = 2;
|
|
20
23
|
|
|
21
|
-
export { CONTAINER_MINI_HEIGHT, CONTAINER_MINI_WIDTH, DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY, DEFAULT_WAIT_FOR_NETWORK_IDLE_TIME, DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT, NodeType, PLAYGROUND_SERVER_PORT, SCRCPY_SERVER_PORT, TEXT_MAX_SIZE, TEXT_SIZE_THRESHOLD };
|
|
24
|
+
export { CONTAINER_MINI_HEIGHT, CONTAINER_MINI_WIDTH, DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY, DEFAULT_WAIT_FOR_NETWORK_IDLE_TIME, DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT, NodeType, PLAYGROUND_SERVER_PORT, PLAYWRIGHT_EXAMPLE_CODE, SCRCPY_SERVER_PORT, TEXT_MAX_SIZE, TEXT_SIZE_THRESHOLD, YAML_EXAMPLE_CODE };
|
package/dist/lib/constants.js
CHANGED
|
@@ -28,11 +28,116 @@ __export(constants_exports, {
|
|
|
28
28
|
DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT: () => DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,
|
|
29
29
|
NodeType: () => NodeType,
|
|
30
30
|
PLAYGROUND_SERVER_PORT: () => PLAYGROUND_SERVER_PORT,
|
|
31
|
+
PLAYWRIGHT_EXAMPLE_CODE: () => PLAYWRIGHT_EXAMPLE_CODE,
|
|
31
32
|
SCRCPY_SERVER_PORT: () => SCRCPY_SERVER_PORT,
|
|
32
33
|
TEXT_MAX_SIZE: () => TEXT_MAX_SIZE,
|
|
33
|
-
TEXT_SIZE_THRESHOLD: () => TEXT_SIZE_THRESHOLD
|
|
34
|
+
TEXT_SIZE_THRESHOLD: () => TEXT_SIZE_THRESHOLD,
|
|
35
|
+
YAML_EXAMPLE_CODE: () => YAML_EXAMPLE_CODE
|
|
34
36
|
});
|
|
35
37
|
module.exports = __toCommonJS(constants_exports);
|
|
38
|
+
|
|
39
|
+
// src/constants/example-code.ts
|
|
40
|
+
var PLAYWRIGHT_EXAMPLE_CODE = `
|
|
41
|
+
// Reference the following code to generate Midscene test cases
|
|
42
|
+
// The following is test code for Midscene AI, for reference
|
|
43
|
+
// The following is Playwright syntax, you can use Playwright to assist in test generation
|
|
44
|
+
IMPORTANT: Follow these exact type signatures for AI functions:
|
|
45
|
+
|
|
46
|
+
// Type signatures for AI functions:
|
|
47
|
+
aiInput(value: string, locator: string): Promise<void>
|
|
48
|
+
aiTap(locator: string): Promise<void>
|
|
49
|
+
aiAssert(assertion: string): Promise<void>
|
|
50
|
+
aiQuery<T>(queryObject: Record<string, string>): Promise<T> // Extracts data from page based on descriptions
|
|
51
|
+
|
|
52
|
+
// examples:
|
|
53
|
+
// Reference the following code to generate Midscene test cases
|
|
54
|
+
// The following is test code for Midscene AI, for reference
|
|
55
|
+
// The following is Playwright syntax, you can use Playwright to assist in test generation
|
|
56
|
+
import { test as base } from '@playwright/test';
|
|
57
|
+
import type { PlayWrightAiFixtureType } from '@midscene/web/playwright';
|
|
58
|
+
import { PlaywrightAiFixture } from '@midscene/web/playwright';
|
|
59
|
+
|
|
60
|
+
export const test = base.extend<PlayWrightAiFixtureType>(PlaywrightAiFixture({
|
|
61
|
+
waitForNetworkIdleTimeout: 2000, // optional, the timeout for waiting for network idle between each action, default is 2000ms
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
test.beforeEach(async ({ page }) => {
|
|
66
|
+
await page.goto('https://www.xxx.com/');
|
|
67
|
+
await page.setViewportSize({ width: 1920, height: 1080 });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('ai shop', async ({
|
|
71
|
+
aiInput,
|
|
72
|
+
aiAssert,
|
|
73
|
+
aiQuery,
|
|
74
|
+
aiKeyboardPress,
|
|
75
|
+
aiHover,
|
|
76
|
+
aiTap,
|
|
77
|
+
agentForPage,
|
|
78
|
+
page,
|
|
79
|
+
}) => {
|
|
80
|
+
// login
|
|
81
|
+
await aiAssert('The page shows the login interface');
|
|
82
|
+
await aiInput('user_name', 'in user name input');
|
|
83
|
+
await aiInput('password', 'in password input');
|
|
84
|
+
await aiKeyboardPress('Enter', 'Login Button');
|
|
85
|
+
|
|
86
|
+
// check the login success
|
|
87
|
+
await aiWaitFor('The page shows that the loading is complete');
|
|
88
|
+
await aiAssert('The current page shows the product detail page');
|
|
89
|
+
|
|
90
|
+
// check the product info
|
|
91
|
+
const dataA = await aiQuery({
|
|
92
|
+
userInfo: 'User information in the format {name: string}',
|
|
93
|
+
theFirstProductInfo: 'The first product info in the format {name: string, price: number}',
|
|
94
|
+
});
|
|
95
|
+
expect(dataA.theFirstProductInfo.name).toBe('xxx');
|
|
96
|
+
expect(dataA.theFirstProductInfo.price).toBe(100);
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
// add to cart
|
|
100
|
+
await aiTap('click add to cart button');
|
|
101
|
+
|
|
102
|
+
await aiTap('click right top cart icon');
|
|
103
|
+
await aiAssert('The cart icon shows the number 1');
|
|
104
|
+
});
|
|
105
|
+
`;
|
|
106
|
+
var YAML_EXAMPLE_CODE = `
|
|
107
|
+
1. Format:
|
|
108
|
+
|
|
109
|
+
target:
|
|
110
|
+
url: "starting_url"
|
|
111
|
+
viewportWidth: 1280
|
|
112
|
+
viewportHeight: 960
|
|
113
|
+
|
|
114
|
+
tasks:
|
|
115
|
+
- name: "descriptive task name"
|
|
116
|
+
flow:
|
|
117
|
+
- aiTap: "element description"
|
|
118
|
+
- aiInput: 'text value'
|
|
119
|
+
locate: 'input field description'
|
|
120
|
+
- aiScroll:
|
|
121
|
+
direction: down/up
|
|
122
|
+
scrollType: untilBottom/untilTop/page
|
|
123
|
+
- aiAssert: "expected state"
|
|
124
|
+
- sleep: milliseconds
|
|
125
|
+
|
|
126
|
+
2. Action Types:
|
|
127
|
+
- aiTap: for clicks (natural language targeting)
|
|
128
|
+
- aiInput: for text input with 'locate' field
|
|
129
|
+
- aiScroll: with direction and scrollType
|
|
130
|
+
- aiAssert: for validations
|
|
131
|
+
- sleep: for delays (milliseconds)
|
|
132
|
+
|
|
133
|
+
3. Best Practices:
|
|
134
|
+
- Group related actions into logical tasks
|
|
135
|
+
- Use natural language descriptions
|
|
136
|
+
- Add deepThink: true for complex interactions
|
|
137
|
+
- Keep task names concise but descriptive
|
|
138
|
+
`;
|
|
139
|
+
|
|
140
|
+
// src/constants/index.ts
|
|
36
141
|
var TEXT_SIZE_THRESHOLD = 9;
|
|
37
142
|
var TEXT_MAX_SIZE = 40;
|
|
38
143
|
var CONTAINER_MINI_HEIGHT = 3;
|
|
@@ -63,7 +168,9 @@ var DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY = 2;
|
|
|
63
168
|
DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,
|
|
64
169
|
NodeType,
|
|
65
170
|
PLAYGROUND_SERVER_PORT,
|
|
171
|
+
PLAYWRIGHT_EXAMPLE_CODE,
|
|
66
172
|
SCRCPY_SERVER_PORT,
|
|
67
173
|
TEXT_MAX_SIZE,
|
|
68
|
-
TEXT_SIZE_THRESHOLD
|
|
174
|
+
TEXT_SIZE_THRESHOLD,
|
|
175
|
+
YAML_EXAMPLE_CODE
|
|
69
176
|
});
|