@midscene/web 0.0.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/index.js +442 -0
- package/dist/lib/index.js +473 -0
- package/dist/script/htmlElement.js +272 -0
- package/dist/script/types/htmlElement.d.ts +26 -0
- package/dist/types/index.d.ts +66 -0
- package/modern.config.ts +13 -0
- package/modern.inspect.config.ts +20 -0
- package/package.json +85 -0
- package/playwright.config.ts +42 -0
- package/src/html-element/constants.ts +10 -0
- package/src/html-element/debug.ts +3 -0
- package/src/html-element/dom-util.ts +11 -0
- package/src/html-element/extractInfo.ts +168 -0
- package/src/html-element/index.ts +1 -0
- package/src/html-element/util.ts +160 -0
- package/src/img/img.ts +132 -0
- package/src/img/util.ts +28 -0
- package/src/index.ts +2 -0
- package/src/playwright/actions.ts +276 -0
- package/src/playwright/cdp.ts +322 -0
- package/src/playwright/element.ts +74 -0
- package/src/playwright/index.ts +120 -0
- package/src/playwright/utils.ts +88 -0
- package/src/puppeteer/element.ts +49 -0
- package/src/puppeteer/index.ts +6 -0
- package/src/puppeteer/utils.ts +116 -0
- package/tests/e2e/ai-auto-todo.spec.ts +24 -0
- package/tests/e2e/ai-xicha.spec.ts +34 -0
- package/tests/e2e/fixture.ts +6 -0
- package/tests/e2e/generate-test-data.spec.ts +60 -0
- package/tests/e2e/todo-app-midscene.spec.ts +98 -0
- package/tests/e2e/tool.ts +63 -0
- package/tsconfig.json +23 -0
- package/vitest.config.ts +14 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var midscene_element_inspector = (() => {
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __defProps = Object.defineProperties;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
11
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
12
|
+
var __spreadValues = (a, b) => {
|
|
13
|
+
for (var prop in b || (b = {}))
|
|
14
|
+
if (__hasOwnProp.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
if (__getOwnPropSymbols)
|
|
17
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
18
|
+
if (__propIsEnum.call(b, prop))
|
|
19
|
+
__defNormalProp(a, prop, b[prop]);
|
|
20
|
+
}
|
|
21
|
+
return a;
|
|
22
|
+
};
|
|
23
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
24
|
+
var __export = (target, all) => {
|
|
25
|
+
for (var name in all)
|
|
26
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
27
|
+
};
|
|
28
|
+
var __copyProps = (to, from, except, desc) => {
|
|
29
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
30
|
+
for (let key of __getOwnPropNames(from))
|
|
31
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
32
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
33
|
+
}
|
|
34
|
+
return to;
|
|
35
|
+
};
|
|
36
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
37
|
+
|
|
38
|
+
// src/html-element/index.ts
|
|
39
|
+
var html_element_exports = {};
|
|
40
|
+
__export(html_element_exports, {
|
|
41
|
+
extractTextWithPositionDFS: () => extractTextWithPositionDFS
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// src/html-element/util.ts
|
|
45
|
+
function logger(...msg) {
|
|
46
|
+
}
|
|
47
|
+
var taskIdKey = "_midscene_retrieve_task_id";
|
|
48
|
+
function selectorForValue(val) {
|
|
49
|
+
return `[${taskIdKey}='${val}']`;
|
|
50
|
+
}
|
|
51
|
+
function setDataForNode(node, nodeIndex) {
|
|
52
|
+
const taskId = taskIdKey;
|
|
53
|
+
if (!(node instanceof HTMLElement)) {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
if (!taskId) {
|
|
57
|
+
console.error("No task id found");
|
|
58
|
+
return "";
|
|
59
|
+
}
|
|
60
|
+
const selector = selectorForValue(nodeIndex);
|
|
61
|
+
node.setAttribute(taskIdKey, nodeIndex.toString());
|
|
62
|
+
return selector;
|
|
63
|
+
}
|
|
64
|
+
function getPseudoElementContent(element) {
|
|
65
|
+
if (!(element instanceof HTMLElement)) {
|
|
66
|
+
return { before: "", after: "" };
|
|
67
|
+
}
|
|
68
|
+
const beforeContent = window.getComputedStyle(element, "::before").getPropertyValue("content");
|
|
69
|
+
const afterContent = window.getComputedStyle(element, "::after").getPropertyValue("content");
|
|
70
|
+
return {
|
|
71
|
+
before: beforeContent === "none" ? "" : beforeContent.replace(/"/g, ""),
|
|
72
|
+
after: afterContent === "none" ? "" : afterContent.replace(/"/g, "")
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function visibleRect(el) {
|
|
76
|
+
if (!el) {
|
|
77
|
+
logger("Element is not in the DOM hierarchy");
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (!(el instanceof HTMLElement)) {
|
|
81
|
+
logger("Element is not in the DOM hierarchy");
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const style = window.getComputedStyle(el);
|
|
85
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0" && el.tagName !== "INPUT") {
|
|
86
|
+
logger("Element is hidden");
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const rect = el.getBoundingClientRect();
|
|
90
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
91
|
+
logger("Element has no size");
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
|
|
95
|
+
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
96
|
+
const isInViewport = rect.top >= 0 + scrollTop && rect.left >= 0 + scrollLeft && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) + scrollTop && rect.right <= (window.innerWidth || document.documentElement.clientWidth) + scrollLeft;
|
|
97
|
+
if (!isInViewport) {
|
|
98
|
+
logger("Element is not in the viewport");
|
|
99
|
+
logger(rect, window.innerHeight, window.innerWidth, scrollTop, scrollLeft);
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
let parent = el;
|
|
103
|
+
while (parent && parent !== document.body) {
|
|
104
|
+
const parentStyle = window.getComputedStyle(parent);
|
|
105
|
+
if (parentStyle.overflow === "hidden") {
|
|
106
|
+
const parentRect = parent.getBoundingClientRect();
|
|
107
|
+
const tolerance = 10;
|
|
108
|
+
if (rect.top < parentRect.top - tolerance || rect.left < parentRect.left - tolerance || rect.bottom > parentRect.bottom + tolerance || rect.right > parentRect.right + tolerance) {
|
|
109
|
+
logger("Element is clipped by an ancestor", parent, rect, parentRect);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
parent = parent.parentElement;
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
left: Math.round(rect.left - scrollLeft),
|
|
117
|
+
top: Math.round(rect.top - scrollTop),
|
|
118
|
+
width: Math.round(rect.width),
|
|
119
|
+
height: Math.round(rect.height)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
function validTextNodeContent(node) {
|
|
123
|
+
if (!node) {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
console.log("node", node);
|
|
127
|
+
if (node.nodeType === Node.COMMENT_NODE) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const everyChildNodeIsText = Array.from(node.childNodes).findIndex(
|
|
131
|
+
(child) => child.nodeType === Node.TEXT_NODE
|
|
132
|
+
);
|
|
133
|
+
if (everyChildNodeIsText === -1) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
const content = node.textContent || node.innerText;
|
|
137
|
+
if (content && !/^\s*$/.test(content)) {
|
|
138
|
+
return content.trim();
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
function getNodeAttributes(node) {
|
|
143
|
+
if (!node || !(node instanceof HTMLElement) || !node.attributes) {
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
146
|
+
const attributesList = Array.from(node.attributes).map((attr) => {
|
|
147
|
+
if (attr.name === "class") {
|
|
148
|
+
return [attr.name, `.${attr.value.split(" ").join(".")}`];
|
|
149
|
+
}
|
|
150
|
+
if (!attr.value) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
return [attr.name, attr.value];
|
|
154
|
+
});
|
|
155
|
+
return Object.fromEntries(attributesList);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/html-element/constants.ts
|
|
159
|
+
var TEXT_SIZE_THRESHOLD = 9;
|
|
160
|
+
|
|
161
|
+
// src/html-element/dom-util.ts
|
|
162
|
+
function isInputElement(node) {
|
|
163
|
+
return node instanceof HTMLElement && node.tagName.toLowerCase() === "input";
|
|
164
|
+
}
|
|
165
|
+
function isButtonElement(node) {
|
|
166
|
+
return node instanceof HTMLElement && node.tagName.toLowerCase() === "button";
|
|
167
|
+
}
|
|
168
|
+
function isImgElement(node) {
|
|
169
|
+
return node instanceof HTMLElement && node.tagName.toLowerCase() === "img";
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/html-element/extractInfo.ts
|
|
173
|
+
var container = document.body;
|
|
174
|
+
function generateId(numberId) {
|
|
175
|
+
return `${numberId}`;
|
|
176
|
+
}
|
|
177
|
+
function extractTextWithPositionDFS(initNode = container) {
|
|
178
|
+
const elementInfoArray = [];
|
|
179
|
+
const nodeMapTree = { node: initNode, childrens: [] };
|
|
180
|
+
let nodeIndex = 1;
|
|
181
|
+
function dfs(node, parentNode = null) {
|
|
182
|
+
if (!node) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const currentNodeDes = { node, childrens: [] };
|
|
186
|
+
if (parentNode == null ? void 0 : parentNode.childrens) {
|
|
187
|
+
parentNode.childrens.push(currentNodeDes);
|
|
188
|
+
}
|
|
189
|
+
collectElementInfo(node);
|
|
190
|
+
for (let i = 0; i < node.childNodes.length; i++) {
|
|
191
|
+
logger("will dfs", node.childNodes[i]);
|
|
192
|
+
dfs(node.childNodes[i], currentNodeDes);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function collectElementInfo(node) {
|
|
196
|
+
const rect = visibleRect(node);
|
|
197
|
+
if (!rect) {
|
|
198
|
+
logger("Element is not visible", node);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (isInputElement(node)) {
|
|
202
|
+
const attributes = getNodeAttributes(node);
|
|
203
|
+
const selector = setDataForNode(node, nodeIndex);
|
|
204
|
+
elementInfoArray.push({
|
|
205
|
+
id: generateId(nodeIndex++),
|
|
206
|
+
locator: selector,
|
|
207
|
+
attributes: __spreadProps(__spreadValues({}, attributes), {
|
|
208
|
+
nodeType: "INPUT Node" /* INPUT */
|
|
209
|
+
}),
|
|
210
|
+
content: attributes.placeholder || "",
|
|
211
|
+
rect,
|
|
212
|
+
center: [Math.round(rect.left + rect.width / 2), Math.round(rect.top + rect.height / 2)]
|
|
213
|
+
});
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (isButtonElement(node)) {
|
|
217
|
+
const attributes = getNodeAttributes(node);
|
|
218
|
+
const pseudo = getPseudoElementContent(node);
|
|
219
|
+
const selector = setDataForNode(node, nodeIndex);
|
|
220
|
+
elementInfoArray.push({
|
|
221
|
+
id: generateId(nodeIndex++),
|
|
222
|
+
locator: selector,
|
|
223
|
+
attributes: __spreadProps(__spreadValues({}, attributes), {
|
|
224
|
+
nodeType: "BUTTON Node" /* BUTTON */
|
|
225
|
+
}),
|
|
226
|
+
content: node.innerText || pseudo.before || pseudo.after || "",
|
|
227
|
+
rect,
|
|
228
|
+
center: [Math.round(rect.left + rect.width / 2), Math.round(rect.top + rect.height / 2)]
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (isImgElement(node)) {
|
|
233
|
+
const attributes = getNodeAttributes(node);
|
|
234
|
+
const selector = setDataForNode(node, nodeIndex);
|
|
235
|
+
elementInfoArray.push({
|
|
236
|
+
id: generateId(nodeIndex++),
|
|
237
|
+
locator: selector,
|
|
238
|
+
attributes: __spreadProps(__spreadValues({}, attributes), {
|
|
239
|
+
nodeType: "IMG Node" /* IMG */
|
|
240
|
+
}),
|
|
241
|
+
content: "",
|
|
242
|
+
rect,
|
|
243
|
+
center: [Math.round(rect.left + rect.width / 2), Math.round(rect.top + rect.height / 2)]
|
|
244
|
+
});
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const text = validTextNodeContent(node);
|
|
248
|
+
if (text) {
|
|
249
|
+
if (rect.width < TEXT_SIZE_THRESHOLD || rect.height < TEXT_SIZE_THRESHOLD) {
|
|
250
|
+
logger("Element is too small", text);
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const attributes = getNodeAttributes(node);
|
|
254
|
+
const selector = setDataForNode(node, nodeIndex);
|
|
255
|
+
elementInfoArray.push({
|
|
256
|
+
id: generateId(nodeIndex++),
|
|
257
|
+
attributes: __spreadProps(__spreadValues({}, attributes), {
|
|
258
|
+
nodeType: "TEXT Node" /* TEXT */
|
|
259
|
+
}),
|
|
260
|
+
locator: selector,
|
|
261
|
+
center: [Math.round(rect.left + rect.width / 2), Math.round(rect.top + rect.height / 2)],
|
|
262
|
+
// attributes,
|
|
263
|
+
content: text,
|
|
264
|
+
rect
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
dfs(initNode, nodeMapTree);
|
|
269
|
+
return elementInfoArray;
|
|
270
|
+
}
|
|
271
|
+
return __toCommonJS(html_element_exports);
|
|
272
|
+
})();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
declare enum NodeType {
|
|
2
|
+
'INPUT' = "INPUT Node",
|
|
3
|
+
'BUTTON' = "BUTTON Node",
|
|
4
|
+
'IMG' = "IMG Node",
|
|
5
|
+
'TEXT' = "TEXT Node"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface ElementInfo {
|
|
9
|
+
id: string;
|
|
10
|
+
locator: string | void;
|
|
11
|
+
attributes: {
|
|
12
|
+
nodeType: NodeType;
|
|
13
|
+
[key: string]: string;
|
|
14
|
+
};
|
|
15
|
+
content: string;
|
|
16
|
+
rect: {
|
|
17
|
+
left: number;
|
|
18
|
+
top: number;
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
};
|
|
22
|
+
center: [number, number];
|
|
23
|
+
}
|
|
24
|
+
declare function extractTextWithPositionDFS(initNode?: Node): ElementInfo[];
|
|
25
|
+
|
|
26
|
+
export { extractTextWithPositionDFS };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { TestInfo } from '@playwright/test';
|
|
2
|
+
import { Page } from 'playwright';
|
|
3
|
+
import Insight, { BaseElement, Rect, Executor, ExecutionDump, InsightExtractParam } from '@midscene/core';
|
|
4
|
+
|
|
5
|
+
declare enum NodeType {
|
|
6
|
+
'INPUT' = "INPUT Node",
|
|
7
|
+
'BUTTON' = "BUTTON Node",
|
|
8
|
+
'IMG' = "IMG Node",
|
|
9
|
+
'TEXT' = "TEXT Node"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
declare class WebElementInfo implements BaseElement {
|
|
13
|
+
content: string;
|
|
14
|
+
locator: string;
|
|
15
|
+
rect: Rect;
|
|
16
|
+
center: [number, number];
|
|
17
|
+
page: Page;
|
|
18
|
+
id: string;
|
|
19
|
+
attributes: {
|
|
20
|
+
['nodeType']: NodeType;
|
|
21
|
+
[key: string]: string;
|
|
22
|
+
};
|
|
23
|
+
constructor({ content, rect, page, locator, id, attributes, }: {
|
|
24
|
+
content: string;
|
|
25
|
+
rect: Rect;
|
|
26
|
+
page: Page;
|
|
27
|
+
locator: string;
|
|
28
|
+
id: string;
|
|
29
|
+
attributes: {
|
|
30
|
+
['nodeType']: NodeType;
|
|
31
|
+
[key: string]: string;
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
tap(): Promise<void>;
|
|
35
|
+
hover(): Promise<void>;
|
|
36
|
+
type(text: string): Promise<void>;
|
|
37
|
+
press(key: Parameters<typeof this$1.page.keyboard.press>[0]): Promise<void>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
declare class PlayWrightActionAgent {
|
|
41
|
+
page: Page;
|
|
42
|
+
insight: Insight<WebElementInfo>;
|
|
43
|
+
executor: Executor;
|
|
44
|
+
actionDump?: ExecutionDump;
|
|
45
|
+
constructor(page: Page, opt?: {
|
|
46
|
+
taskName?: string;
|
|
47
|
+
});
|
|
48
|
+
private recordScreenshot;
|
|
49
|
+
private wrapExecutorWithScreenshot;
|
|
50
|
+
private convertPlanToExecutable;
|
|
51
|
+
action(userPrompt: string): Promise<void>;
|
|
52
|
+
query(demand: InsightExtractParam): Promise<any>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare const PlaywrightAiFixture: () => {
|
|
56
|
+
ai: ({ page }: any, use: any, testInfo: TestInfo) => Promise<void>;
|
|
57
|
+
aiAction: ({ page }: any, use: any, testInfo: TestInfo) => Promise<void>;
|
|
58
|
+
aiQuery: ({ page }: any, use: any, testInfo: TestInfo) => Promise<void>;
|
|
59
|
+
};
|
|
60
|
+
type PlayWrightAiFixtureType = {
|
|
61
|
+
ai: <T = any>(prompt: string, type?: 'action' | 'query') => Promise<T>;
|
|
62
|
+
aiAction: (taskPrompt: string) => ReturnType<PlayWrightActionAgent['action']>;
|
|
63
|
+
aiQuery: <T = any>(demand: any) => Promise<T>;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export { type PlayWrightAiFixtureType, PlaywrightAiFixture };
|
package/modern.config.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { moduleTools, defineConfig } from '@modern-js/module-tools';
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
plugins: [moduleTools()],
|
|
5
|
+
buildPreset: 'npm-library',
|
|
6
|
+
buildConfig: {
|
|
7
|
+
platform: 'node',
|
|
8
|
+
input: {
|
|
9
|
+
index: 'src/index.ts',
|
|
10
|
+
},
|
|
11
|
+
target: 'es2017',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { moduleTools, defineConfig } from '@modern-js/module-tools';
|
|
2
|
+
|
|
3
|
+
// It was split into two configuration files because of a bug in the build config array
|
|
4
|
+
export default defineConfig({
|
|
5
|
+
plugins: [moduleTools()],
|
|
6
|
+
buildPreset: 'npm-library',
|
|
7
|
+
buildConfig: {
|
|
8
|
+
buildType: 'bundle',
|
|
9
|
+
format: 'iife',
|
|
10
|
+
input: {
|
|
11
|
+
htmlElement:'src/html-element/index.ts',
|
|
12
|
+
},
|
|
13
|
+
outDir: 'dist/script',
|
|
14
|
+
esbuildOptions: options => {
|
|
15
|
+
options.globalName = 'midscene_element_inspector'
|
|
16
|
+
return options;
|
|
17
|
+
},
|
|
18
|
+
target: 'es2017',
|
|
19
|
+
}
|
|
20
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@midscene/web",
|
|
3
|
+
"description": "Web integration for MidScene.js",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"jsnext:source": "./src/index.ts",
|
|
6
|
+
"main": "./dist/lib/index.js",
|
|
7
|
+
"module": "./dist/es/index.js",
|
|
8
|
+
"types": "./dist/types/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/types/index.d.ts",
|
|
12
|
+
"import": "./dist/es/index.js",
|
|
13
|
+
"require": "./dist/lib/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./constants": {
|
|
16
|
+
"types": "./dist/types/constants.d.ts",
|
|
17
|
+
"import": "./dist/es/constants.js",
|
|
18
|
+
"require": "./dist/lib/constants.js"
|
|
19
|
+
},
|
|
20
|
+
"./html-element": {
|
|
21
|
+
"types": "./dist/types/html-element/index.d.ts",
|
|
22
|
+
"import": "./dist/es/html-element/index.js",
|
|
23
|
+
"require": "./dist/lib/html-element/index.js"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"typesVersions": {
|
|
27
|
+
"*": {
|
|
28
|
+
".": [
|
|
29
|
+
"./dist/types/index.d.ts"
|
|
30
|
+
],
|
|
31
|
+
"constants": [
|
|
32
|
+
"./dist/types/constants.d.ts"
|
|
33
|
+
],
|
|
34
|
+
"html-element": [
|
|
35
|
+
"./dist/types/html-element/index.d.ts"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"openai": "4.47.1",
|
|
41
|
+
"sharp": "0.33.3",
|
|
42
|
+
"@midscene/core": "0.0.1"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@modern-js/module-tools": "^2.54.2",
|
|
46
|
+
"@types/node": "^18.0.0",
|
|
47
|
+
"typescript": "~5.0.4",
|
|
48
|
+
"vitest": "^1.6.0",
|
|
49
|
+
"playwright": "1.44.1",
|
|
50
|
+
"puppeteer": "^22.8.0",
|
|
51
|
+
"@playwright/test": "1.44.1"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"@playwright/test": "^1.44.1",
|
|
55
|
+
"playwright": "^1.44.1",
|
|
56
|
+
"puppeteer": "^22.8.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependenciesMeta": {
|
|
59
|
+
"@playwright/test": {
|
|
60
|
+
"optional": true
|
|
61
|
+
},
|
|
62
|
+
"puppeteer": {
|
|
63
|
+
"optional": true
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": ">=16.0.0"
|
|
68
|
+
},
|
|
69
|
+
"publishConfig": {
|
|
70
|
+
"access": "public",
|
|
71
|
+
"registry": "https://registry.npmjs.org"
|
|
72
|
+
},
|
|
73
|
+
"license": "MIT",
|
|
74
|
+
"scripts": {
|
|
75
|
+
"dev": "modern dev",
|
|
76
|
+
"build": "npm run build:pkg && npm run build:script",
|
|
77
|
+
"build:pkg": "modern build -c ./modern.config.ts",
|
|
78
|
+
"build:script": "modern build -c ./modern.inspect.config.ts",
|
|
79
|
+
"build:watch": "modern build -w -c ./modern.config.ts & modern build -w -c ./modern.inspect.config.ts",
|
|
80
|
+
"new": "modern new",
|
|
81
|
+
"upgrade": "modern upgrade",
|
|
82
|
+
"e2e": "playwright test --config=playwright.config.ts",
|
|
83
|
+
"e2e:ui": "playwright test --config=playwright.config.ts --ui"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Read environment variables from file.
|
|
5
|
+
* https://github.com/motdotla/dotenv
|
|
6
|
+
*/
|
|
7
|
+
// require('dotenv').config();
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* See https://playwright.dev/docs/test-configuration.
|
|
11
|
+
*/
|
|
12
|
+
export default defineConfig({
|
|
13
|
+
testDir: './tests/e2e',
|
|
14
|
+
timeout: 90000 * 1000,
|
|
15
|
+
/* Run tests in files in parallel */
|
|
16
|
+
fullyParallel: true,
|
|
17
|
+
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
|
18
|
+
forbidOnly: !!process.env.CI,
|
|
19
|
+
/* Retry on CI only */
|
|
20
|
+
retries: process.env.CI ? 2 : 0,
|
|
21
|
+
/* Opt out of parallel tests on CI. */
|
|
22
|
+
workers: process.env.CI ? 1 : undefined,
|
|
23
|
+
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
|
24
|
+
reporter: 'html',
|
|
25
|
+
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
|
26
|
+
use: {
|
|
27
|
+
/* Base URL to use in actions like `await page.goto('/')`. */
|
|
28
|
+
// baseURL: 'http://127.0.0.1:3000',
|
|
29
|
+
|
|
30
|
+
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
|
31
|
+
trace: 'on-first-retry',
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
/* Configure projects for major browsers */
|
|
35
|
+
projects: [
|
|
36
|
+
{
|
|
37
|
+
name: 'chromium',
|
|
38
|
+
use: { ...devices['Desktop Chrome'] },
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
|
|
42
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function isInputElement(node: Node): node is HTMLInputElement {
|
|
2
|
+
return node instanceof HTMLElement && node.tagName.toLowerCase() === 'input';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function isButtonElement(node: Node): node is HTMLButtonElement {
|
|
6
|
+
return node instanceof HTMLElement && node.tagName.toLowerCase() === 'button';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function isImgElement(node: Node): node is HTMLImageElement {
|
|
10
|
+
return node instanceof HTMLElement && node.tagName.toLowerCase() === 'img';
|
|
11
|
+
}
|