@godscene/web 1.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/bin/midscene-playground +3 -0
- package/bin/midscene-web +2 -0
- package/dist/es/bin.mjs +14 -0
- package/dist/es/bridge-mode/agent-cli-side.mjs +135 -0
- package/dist/es/bridge-mode/browser.mjs +2 -0
- package/dist/es/bridge-mode/common.mjs +41 -0
- package/dist/es/bridge-mode/index.mjs +4 -0
- package/dist/es/bridge-mode/io-client.mjs +99 -0
- package/dist/es/bridge-mode/io-server.mjs +218 -0
- package/dist/es/bridge-mode/page-browser-side.mjs +119 -0
- package/dist/es/cdp-proxy-constants.mjs +7 -0
- package/dist/es/cdp-proxy-manager.mjs +217 -0
- package/dist/es/cdp-proxy.mjs +151 -0
- package/dist/es/cdp-target-store.mjs +26 -0
- package/dist/es/chrome-extension/agent.mjs +8 -0
- package/dist/es/chrome-extension/cdpInput.mjs +172 -0
- package/dist/es/chrome-extension/cdpInput.mjs.LICENSE.txt +5 -0
- package/dist/es/chrome-extension/dynamic-scripts.mjs +36 -0
- package/dist/es/chrome-extension/index.mjs +5 -0
- package/dist/es/chrome-extension/page.mjs +733 -0
- package/dist/es/cli-options.mjs +97 -0
- package/dist/es/cli.mjs +26 -0
- package/dist/es/common/cache-helper.mjs +26 -0
- package/dist/es/common/viewport.mjs +36 -0
- package/dist/es/index.mjs +8 -0
- package/dist/es/mcp-server.mjs +33 -0
- package/dist/es/mcp-tools-cdp.mjs +164 -0
- package/dist/es/mcp-tools-puppeteer.mjs +246 -0
- package/dist/es/mcp-tools.mjs +81 -0
- package/dist/es/platform.mjs +37 -0
- package/dist/es/playwright/ai-fixture.mjs +364 -0
- package/dist/es/playwright/index.mjs +36 -0
- package/dist/es/playwright/page.mjs +42 -0
- package/dist/es/playwright/reporter/index.mjs +178 -0
- package/dist/es/puppeteer/agent-launcher.mjs +172 -0
- package/dist/es/puppeteer/base-page.mjs +830 -0
- package/dist/es/puppeteer/index.mjs +35 -0
- package/dist/es/puppeteer/page.mjs +7 -0
- package/dist/es/static/index.mjs +3 -0
- package/dist/es/static/static-agent.mjs +10 -0
- package/dist/es/static/static-page.mjs +123 -0
- package/dist/es/utils.mjs +6 -0
- package/dist/es/web-element.mjs +57 -0
- package/dist/es/web-page.mjs +272 -0
- package/dist/lib/bin.js +20 -0
- package/dist/lib/bridge-mode/agent-cli-side.js +172 -0
- package/dist/lib/bridge-mode/browser.js +36 -0
- package/dist/lib/bridge-mode/common.js +105 -0
- package/dist/lib/bridge-mode/index.js +44 -0
- package/dist/lib/bridge-mode/io-client.js +133 -0
- package/dist/lib/bridge-mode/io-server.js +255 -0
- package/dist/lib/bridge-mode/page-browser-side.js +163 -0
- package/dist/lib/cdp-proxy-constants.js +50 -0
- package/dist/lib/cdp-proxy-manager.js +273 -0
- package/dist/lib/cdp-proxy.js +179 -0
- package/dist/lib/cdp-target-store.js +66 -0
- package/dist/lib/chrome-extension/agent.js +42 -0
- package/dist/lib/chrome-extension/cdpInput.js +206 -0
- package/dist/lib/chrome-extension/cdpInput.js.LICENSE.txt +5 -0
- package/dist/lib/chrome-extension/dynamic-scripts.js +86 -0
- package/dist/lib/chrome-extension/index.js +58 -0
- package/dist/lib/chrome-extension/page.js +767 -0
- package/dist/lib/cli-options.js +131 -0
- package/dist/lib/cli.js +54 -0
- package/dist/lib/common/cache-helper.js +66 -0
- package/dist/lib/common/viewport.js +88 -0
- package/dist/lib/index.js +66 -0
- package/dist/lib/mcp-server.js +73 -0
- package/dist/lib/mcp-tools-cdp.js +208 -0
- package/dist/lib/mcp-tools-puppeteer.js +296 -0
- package/dist/lib/mcp-tools.js +115 -0
- package/dist/lib/platform.js +71 -0
- package/dist/lib/playwright/ai-fixture.js +401 -0
- package/dist/lib/playwright/index.js +89 -0
- package/dist/lib/playwright/page.js +76 -0
- package/dist/lib/playwright/reporter/index.js +212 -0
- package/dist/lib/puppeteer/agent-launcher.js +240 -0
- package/dist/lib/puppeteer/base-page.js +876 -0
- package/dist/lib/puppeteer/index.js +85 -0
- package/dist/lib/puppeteer/page.js +41 -0
- package/dist/lib/static/index.js +50 -0
- package/dist/lib/static/static-agent.js +44 -0
- package/dist/lib/static/static-page.js +157 -0
- package/dist/lib/utils.js +38 -0
- package/dist/lib/web-element.js +94 -0
- package/dist/lib/web-page.js +322 -0
- package/dist/types/bin.d.ts +1 -0
- package/dist/types/bridge-mode/agent-cli-side.d.ts +49 -0
- package/dist/types/bridge-mode/browser.d.ts +2 -0
- package/dist/types/bridge-mode/common.d.ts +74 -0
- package/dist/types/bridge-mode/index.d.ts +4 -0
- package/dist/types/bridge-mode/io-client.d.ts +10 -0
- package/dist/types/bridge-mode/io-server.d.ts +27 -0
- package/dist/types/bridge-mode/page-browser-side.d.ts +21 -0
- package/dist/types/cdp-proxy-constants.d.ts +4 -0
- package/dist/types/cdp-proxy-manager.d.ts +53 -0
- package/dist/types/cdp-proxy.d.ts +37 -0
- package/dist/types/cdp-target-store.d.ts +26 -0
- package/dist/types/chrome-extension/agent.d.ts +4 -0
- package/dist/types/chrome-extension/cdpInput.d.ts +52 -0
- package/dist/types/chrome-extension/dynamic-scripts.d.ts +3 -0
- package/dist/types/chrome-extension/index.d.ts +5 -0
- package/dist/types/chrome-extension/page.d.ts +120 -0
- package/dist/types/cli-options.d.ts +8 -0
- package/dist/types/cli.d.ts +1 -0
- package/dist/types/common/cache-helper.d.ts +20 -0
- package/dist/types/common/viewport.d.ts +17 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/mcp-server.d.ts +26 -0
- package/dist/types/mcp-tools-cdp.d.ts +23 -0
- package/dist/types/mcp-tools-puppeteer.d.ts +23 -0
- package/dist/types/mcp-tools.d.ts +14 -0
- package/dist/types/platform.d.ts +10 -0
- package/dist/types/playwright/ai-fixture.d.ts +133 -0
- package/dist/types/playwright/index.d.ts +13 -0
- package/dist/types/playwright/page.d.ts +11 -0
- package/dist/types/playwright/reporter/index.d.ts +28 -0
- package/dist/types/puppeteer/agent-launcher.d.ts +59 -0
- package/dist/types/puppeteer/base-page.d.ts +123 -0
- package/dist/types/puppeteer/index.d.ts +11 -0
- package/dist/types/puppeteer/page.d.ts +6 -0
- package/dist/types/static/index.d.ts +2 -0
- package/dist/types/static/static-agent.d.ts +5 -0
- package/dist/types/static/static-page.d.ts +46 -0
- package/dist/types/utils.d.ts +6 -0
- package/dist/types/web-element.d.ts +48 -0
- package/dist/types/web-page.d.ts +69 -0
- package/package.json +173 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Agent } from "@godscene/core/agent";
|
|
2
|
+
import { getDebug } from "@godscene/shared/logger";
|
|
3
|
+
import semver from "semver";
|
|
4
|
+
import { getWebpackRequire } from "../utils.mjs";
|
|
5
|
+
import { BROWSER_NAVIGATION_ERROR_PATTERN, forceChromeSelectRendering as external_base_page_mjs_forceChromeSelectRendering, forceClosePopup } from "./base-page.mjs";
|
|
6
|
+
import { PuppeteerWebPage } from "./page.mjs";
|
|
7
|
+
import { overrideAIConfig } from "@godscene/shared/env";
|
|
8
|
+
const debug = getDebug('puppeteer:agent');
|
|
9
|
+
function getPuppeteerVersion() {
|
|
10
|
+
try {
|
|
11
|
+
const puppeteerPkg = getWebpackRequire()('puppeteer/package.json');
|
|
12
|
+
return puppeteerPkg.version || null;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error('[midscene:error] Failed to get Puppeteer version', error);
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
class PuppeteerAgent extends Agent {
|
|
19
|
+
isRetryableContextError(error) {
|
|
20
|
+
return error instanceof Error && BROWSER_NAVIGATION_ERROR_PATTERN.test(error.message);
|
|
21
|
+
}
|
|
22
|
+
constructor(page, opts){
|
|
23
|
+
if (!page) throw new Error('[midscene] PuppeteerAgent requires a valid Puppeteer page instance. Please make sure to pass a valid page object.');
|
|
24
|
+
const webPage = new PuppeteerWebPage(page, opts);
|
|
25
|
+
super(webPage, opts);
|
|
26
|
+
const { forceSameTabNavigation = true, forceChromeSelectRendering } = opts ?? {};
|
|
27
|
+
if (forceSameTabNavigation) forceClosePopup(page, debug);
|
|
28
|
+
if (forceChromeSelectRendering) {
|
|
29
|
+
const puppeteerVersion = getPuppeteerVersion();
|
|
30
|
+
if (puppeteerVersion && !semver.gte(puppeteerVersion, '24.6.0')) console.warn(`[midscene:error] forceChromeSelectRendering requires Puppeteer > 24.6.0, but current version is ${puppeteerVersion}. This feature may not work correctly.`);
|
|
31
|
+
external_base_page_mjs_forceChromeSelectRendering(page);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export { PuppeteerAgent, PuppeteerWebPage, overrideAIConfig };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { defineActionDragAndDrop, defineActionHover, defineActionInput, defineActionKeyboardPress, defineActionRightClick, defineActionScroll, defineActionSwipe, defineActionTap } from "@godscene/core/device";
|
|
2
|
+
import { ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED } from "@godscene/shared/common";
|
|
3
|
+
function _define_property(obj, key, value) {
|
|
4
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
5
|
+
value: value,
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true
|
|
9
|
+
});
|
|
10
|
+
else obj[key] = value;
|
|
11
|
+
return obj;
|
|
12
|
+
}
|
|
13
|
+
const ThrowNotImplemented = (methodName)=>{
|
|
14
|
+
throw new Error(`The method "${methodName}" is not implemented as designed since this is a static UI context. (${ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED})`);
|
|
15
|
+
};
|
|
16
|
+
class StaticPage {
|
|
17
|
+
actionSpace() {
|
|
18
|
+
return [
|
|
19
|
+
defineActionTap(async (param)=>{
|
|
20
|
+
ThrowNotImplemented('Tap');
|
|
21
|
+
}),
|
|
22
|
+
defineActionRightClick(async (param)=>{
|
|
23
|
+
ThrowNotImplemented('RightClick');
|
|
24
|
+
}),
|
|
25
|
+
defineActionHover(async (param)=>{
|
|
26
|
+
ThrowNotImplemented('Hover');
|
|
27
|
+
}),
|
|
28
|
+
defineActionInput(async (param)=>{
|
|
29
|
+
ThrowNotImplemented('Input');
|
|
30
|
+
}),
|
|
31
|
+
defineActionKeyboardPress(async (param)=>{
|
|
32
|
+
ThrowNotImplemented('KeyboardPress');
|
|
33
|
+
}),
|
|
34
|
+
defineActionScroll(async (param)=>{
|
|
35
|
+
ThrowNotImplemented('Scroll');
|
|
36
|
+
}),
|
|
37
|
+
defineActionDragAndDrop(async (param)=>{
|
|
38
|
+
ThrowNotImplemented('DragAndDrop');
|
|
39
|
+
}),
|
|
40
|
+
defineActionSwipe(async (param)=>{
|
|
41
|
+
ThrowNotImplemented('Swipe');
|
|
42
|
+
})
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
async evaluateJavaScript(script) {
|
|
46
|
+
return ThrowNotImplemented('evaluateJavaScript');
|
|
47
|
+
}
|
|
48
|
+
async getElementsInfo() {
|
|
49
|
+
return ThrowNotImplemented('getElementsInfo');
|
|
50
|
+
}
|
|
51
|
+
async getElementsNodeTree() {
|
|
52
|
+
return ThrowNotImplemented('getElementsNodeTree');
|
|
53
|
+
}
|
|
54
|
+
async getXpathsByPoint(point) {
|
|
55
|
+
return ThrowNotImplemented('getXpathsByPoint');
|
|
56
|
+
}
|
|
57
|
+
async getElementFromPoint(args) {
|
|
58
|
+
return ThrowNotImplemented('getElementFromPoint');
|
|
59
|
+
}
|
|
60
|
+
async getElementInfoByXpath(xpath) {
|
|
61
|
+
return ThrowNotImplemented('getElementInfoByXpath');
|
|
62
|
+
}
|
|
63
|
+
async size() {
|
|
64
|
+
return {
|
|
65
|
+
...this.uiContext.shotSize
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
async screenshotBase64() {
|
|
69
|
+
const screenshot = this.uiContext.screenshot;
|
|
70
|
+
if ('object' == typeof screenshot && 'base64' in screenshot) return screenshot.base64;
|
|
71
|
+
return screenshot;
|
|
72
|
+
}
|
|
73
|
+
async url() {
|
|
74
|
+
return Promise.resolve('https://static_page_without_url');
|
|
75
|
+
}
|
|
76
|
+
async scrollUntilTop(startingPoint) {
|
|
77
|
+
return ThrowNotImplemented('scrollUntilTop');
|
|
78
|
+
}
|
|
79
|
+
async scrollUntilBottom(startingPoint) {
|
|
80
|
+
return ThrowNotImplemented('scrollUntilBottom');
|
|
81
|
+
}
|
|
82
|
+
async scrollUntilLeft(startingPoint) {
|
|
83
|
+
return ThrowNotImplemented('scrollUntilLeft');
|
|
84
|
+
}
|
|
85
|
+
async scrollUntilRight(startingPoint) {
|
|
86
|
+
return ThrowNotImplemented('scrollUntilRight');
|
|
87
|
+
}
|
|
88
|
+
async scrollUp(distance, startingPoint) {
|
|
89
|
+
return ThrowNotImplemented('scrollUp');
|
|
90
|
+
}
|
|
91
|
+
async scrollDown(distance, startingPoint) {
|
|
92
|
+
return ThrowNotImplemented('scrollDown');
|
|
93
|
+
}
|
|
94
|
+
async scrollLeft(distance, startingPoint) {
|
|
95
|
+
return ThrowNotImplemented('scrollLeft');
|
|
96
|
+
}
|
|
97
|
+
async scrollRight(distance, startingPoint) {
|
|
98
|
+
return ThrowNotImplemented('scrollRight');
|
|
99
|
+
}
|
|
100
|
+
async clearInput() {
|
|
101
|
+
return ThrowNotImplemented('clearInput');
|
|
102
|
+
}
|
|
103
|
+
async destroy() {}
|
|
104
|
+
updateContext(newContext) {
|
|
105
|
+
this.uiContext = newContext;
|
|
106
|
+
}
|
|
107
|
+
constructor(uiContext){
|
|
108
|
+
_define_property(this, "interfaceType", 'static');
|
|
109
|
+
_define_property(this, "uiContext", void 0);
|
|
110
|
+
_define_property(this, "mouse", {
|
|
111
|
+
click: ThrowNotImplemented.bind(null, 'mouse.click'),
|
|
112
|
+
wheel: ThrowNotImplemented.bind(null, 'mouse.wheel'),
|
|
113
|
+
move: ThrowNotImplemented.bind(null, 'mouse.move'),
|
|
114
|
+
drag: ThrowNotImplemented.bind(null, 'mouse.drag')
|
|
115
|
+
});
|
|
116
|
+
_define_property(this, "keyboard", {
|
|
117
|
+
type: ThrowNotImplemented.bind(null, 'keyboard.type'),
|
|
118
|
+
press: ThrowNotImplemented.bind(null, 'keyboard.press')
|
|
119
|
+
});
|
|
120
|
+
this.uiContext = uiContext;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
export { StaticPage as default };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { createRequire as __rspack_createRequire } from "node:module";
|
|
2
|
+
const __rspack_createRequire_require = __rspack_createRequire(import.meta.url);
|
|
3
|
+
function getWebpackRequire() {
|
|
4
|
+
return void 0 !== __rspack_createRequire_require ? __rspack_createRequire_require : require;
|
|
5
|
+
}
|
|
6
|
+
export { getWebpackRequire };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
3
|
+
value: value,
|
|
4
|
+
enumerable: true,
|
|
5
|
+
configurable: true,
|
|
6
|
+
writable: true
|
|
7
|
+
});
|
|
8
|
+
else obj[key] = value;
|
|
9
|
+
return obj;
|
|
10
|
+
}
|
|
11
|
+
class WebElementInfoImpl {
|
|
12
|
+
constructor({ content, rect, id, attributes, indexId, xpaths, isVisible }){
|
|
13
|
+
_define_property(this, "content", void 0);
|
|
14
|
+
_define_property(this, "rect", void 0);
|
|
15
|
+
_define_property(this, "center", void 0);
|
|
16
|
+
_define_property(this, "id", void 0);
|
|
17
|
+
_define_property(this, "indexId", void 0);
|
|
18
|
+
_define_property(this, "attributes", void 0);
|
|
19
|
+
_define_property(this, "xpaths", void 0);
|
|
20
|
+
_define_property(this, "isVisible", void 0);
|
|
21
|
+
this.content = content;
|
|
22
|
+
this.rect = rect;
|
|
23
|
+
this.center = [
|
|
24
|
+
Math.floor(rect.left + rect.width / 2),
|
|
25
|
+
Math.floor(rect.top + rect.height / 2)
|
|
26
|
+
];
|
|
27
|
+
this.id = id;
|
|
28
|
+
this.attributes = attributes;
|
|
29
|
+
this.indexId = indexId;
|
|
30
|
+
this.xpaths = xpaths;
|
|
31
|
+
this.isVisible = isVisible;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const limitOpenNewTabScript = `
|
|
35
|
+
if (!window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__) {
|
|
36
|
+
window.__MIDSCENE_NEW_TAB_INTERCEPTOR_INITIALIZED__ = true;
|
|
37
|
+
|
|
38
|
+
// Intercept the window.open method (only once)
|
|
39
|
+
window.open = function(url) {
|
|
40
|
+
console.log('Blocked window.open:', url);
|
|
41
|
+
window.location.href = url;
|
|
42
|
+
return null;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Block all a tag clicks with target="_blank" (only once)
|
|
46
|
+
document.addEventListener('click', function(e) {
|
|
47
|
+
const target = e.target.closest('a');
|
|
48
|
+
if (target && target.target === '_blank') {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
console.log('Blocked new tab:', target.href);
|
|
51
|
+
window.location.href = target.href;
|
|
52
|
+
target.removeAttribute('target');
|
|
53
|
+
}
|
|
54
|
+
}, true);
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
export { WebElementInfoImpl, limitOpenNewTabScript };
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import node_assert from "node:assert";
|
|
2
|
+
import { z } from "@godscene/core";
|
|
3
|
+
import { AbstractInterface, defineAction, defineActionClearInput, defineActionCursorMove, defineActionDoubleClick, defineActionDragAndDrop, defineActionHover, defineActionInput, defineActionKeyboardPress, defineActionLongPress, defineActionPinch, defineActionRightClick, defineActionScroll, defineActionSwipe, defineActionTap, normalizePinchParam } from "@godscene/core/device";
|
|
4
|
+
import { sleep } from "@godscene/core/utils";
|
|
5
|
+
import { getDebug } from "@godscene/shared/logger";
|
|
6
|
+
import { transformHotkeyInput } from "@godscene/shared/us-keyboard-layout";
|
|
7
|
+
const debug = getDebug('web:page');
|
|
8
|
+
const navigateParamSchema = z.object({
|
|
9
|
+
url: z.string().describe('The URL to navigate to. Must start with https://, file://, or a similar protocol.')
|
|
10
|
+
});
|
|
11
|
+
function normalizeKeyInputs(value) {
|
|
12
|
+
const inputs = Array.isArray(value) ? value : [
|
|
13
|
+
value
|
|
14
|
+
];
|
|
15
|
+
const result = [];
|
|
16
|
+
for (const input of inputs){
|
|
17
|
+
if ('string' != typeof input) {
|
|
18
|
+
result.push(input);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const trimmed = input.trim();
|
|
22
|
+
if (!trimmed) {
|
|
23
|
+
result.push(input);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
let normalized = trimmed;
|
|
27
|
+
if (normalized.length > 1 && normalized.includes('+')) normalized = normalized.replace(/\s*\+\s*/g, ' ');
|
|
28
|
+
if (/\s/.test(normalized)) normalized = normalized.replace(/\s+/g, ' ');
|
|
29
|
+
const transformed = transformHotkeyInput(normalized);
|
|
30
|
+
if (1 === transformed.length && '' === transformed[0] && '' !== trimmed) {
|
|
31
|
+
result.push(input);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (0 === transformed.length) {
|
|
35
|
+
result.push(input);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
result.push(...transformed);
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
function getKeyCommands(value) {
|
|
43
|
+
const keys = normalizeKeyInputs(value);
|
|
44
|
+
return keys.reduce((acc, k)=>{
|
|
45
|
+
const includeMeta = keys.includes('Meta') || keys.includes('Control');
|
|
46
|
+
if (includeMeta && ('a' === k || 'A' === k)) return acc.concat([
|
|
47
|
+
{
|
|
48
|
+
key: k,
|
|
49
|
+
command: 'SelectAll'
|
|
50
|
+
}
|
|
51
|
+
]);
|
|
52
|
+
if (includeMeta && ('c' === k || 'C' === k)) return acc.concat([
|
|
53
|
+
{
|
|
54
|
+
key: k,
|
|
55
|
+
command: 'Copy'
|
|
56
|
+
}
|
|
57
|
+
]);
|
|
58
|
+
if (includeMeta && ('v' === k || 'V' === k)) return acc.concat([
|
|
59
|
+
{
|
|
60
|
+
key: k,
|
|
61
|
+
command: 'Paste'
|
|
62
|
+
}
|
|
63
|
+
]);
|
|
64
|
+
return acc.concat([
|
|
65
|
+
{
|
|
66
|
+
key: k
|
|
67
|
+
}
|
|
68
|
+
]);
|
|
69
|
+
}, []);
|
|
70
|
+
}
|
|
71
|
+
class AbstractWebPage extends AbstractInterface {
|
|
72
|
+
get mouse() {
|
|
73
|
+
return {
|
|
74
|
+
click: async (x, y, options)=>{},
|
|
75
|
+
wheel: async (deltaX, deltaY)=>{},
|
|
76
|
+
move: async (x, y)=>{},
|
|
77
|
+
drag: async (from, to)=>{}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
get keyboard() {
|
|
81
|
+
return {
|
|
82
|
+
type: async (text)=>{},
|
|
83
|
+
press: async (action)=>{}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
async clearInput(element) {}
|
|
87
|
+
}
|
|
88
|
+
const commonWebActionsForWebPage = (page, includeTouchEvents = false)=>[
|
|
89
|
+
defineActionTap(async (param)=>{
|
|
90
|
+
const element = param.locate;
|
|
91
|
+
node_assert(element, 'Element not found, cannot tap');
|
|
92
|
+
await page.mouse.click(element.center[0], element.center[1], {
|
|
93
|
+
button: 'left'
|
|
94
|
+
});
|
|
95
|
+
}),
|
|
96
|
+
defineActionRightClick(async (param)=>{
|
|
97
|
+
const element = param.locate;
|
|
98
|
+
node_assert(element, 'Element not found, cannot right click');
|
|
99
|
+
await page.mouse.click(element.center[0], element.center[1], {
|
|
100
|
+
button: 'right'
|
|
101
|
+
});
|
|
102
|
+
}),
|
|
103
|
+
defineActionDoubleClick(async (param)=>{
|
|
104
|
+
const element = param.locate;
|
|
105
|
+
node_assert(element, 'Element not found, cannot double click');
|
|
106
|
+
await page.mouse.click(element.center[0], element.center[1], {
|
|
107
|
+
button: 'left',
|
|
108
|
+
count: 2
|
|
109
|
+
});
|
|
110
|
+
}),
|
|
111
|
+
defineActionHover(async (param)=>{
|
|
112
|
+
const element = param.locate;
|
|
113
|
+
node_assert(element, 'Element not found, cannot hover');
|
|
114
|
+
await page.mouse.move(element.center[0], element.center[1]);
|
|
115
|
+
}),
|
|
116
|
+
defineActionInput(async (param)=>{
|
|
117
|
+
const element = param.locate;
|
|
118
|
+
if (element && 'typeOnly' !== param.mode) await page.clearInput(element);
|
|
119
|
+
else if (element && 'typeOnly' === param.mode) {
|
|
120
|
+
await page.mouse.click(element.center[0], element.center[1], {
|
|
121
|
+
button: 'left'
|
|
122
|
+
});
|
|
123
|
+
await page.keyboard.press([
|
|
124
|
+
{
|
|
125
|
+
key: 'End'
|
|
126
|
+
}
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
if ('clear' === param.mode) return;
|
|
130
|
+
if (!param || !param.value) return;
|
|
131
|
+
await page.keyboard.type(param.value);
|
|
132
|
+
await page.flushPendingVisualUpdate?.();
|
|
133
|
+
}),
|
|
134
|
+
defineActionKeyboardPress(async (param)=>{
|
|
135
|
+
const element = param.locate;
|
|
136
|
+
if (element) await page.mouse.click(element.center[0], element.center[1], {
|
|
137
|
+
button: 'left'
|
|
138
|
+
});
|
|
139
|
+
const keys = getKeyCommands(param.keyName);
|
|
140
|
+
await page.keyboard.press(keys);
|
|
141
|
+
await page.flushPendingVisualUpdate?.();
|
|
142
|
+
}),
|
|
143
|
+
defineActionCursorMove(async (param)=>{
|
|
144
|
+
const arrowKey = 'left' === param.direction ? 'ArrowLeft' : 'ArrowRight';
|
|
145
|
+
const times = param.times ?? 1;
|
|
146
|
+
for(let i = 0; i < times; i++){
|
|
147
|
+
await page.keyboard.press([
|
|
148
|
+
{
|
|
149
|
+
key: arrowKey
|
|
150
|
+
}
|
|
151
|
+
]);
|
|
152
|
+
await sleep(100);
|
|
153
|
+
}
|
|
154
|
+
}),
|
|
155
|
+
defineActionScroll(async (param)=>{
|
|
156
|
+
const element = param.locate;
|
|
157
|
+
const startingPoint = element ? {
|
|
158
|
+
left: element.center[0],
|
|
159
|
+
top: element.center[1]
|
|
160
|
+
} : void 0;
|
|
161
|
+
const scrollToEventName = param?.scrollType;
|
|
162
|
+
if ('scrollToTop' === scrollToEventName) await page.scrollUntilTop(startingPoint);
|
|
163
|
+
else if ('scrollToBottom' === scrollToEventName) await page.scrollUntilBottom(startingPoint);
|
|
164
|
+
else if ('scrollToRight' === scrollToEventName) await page.scrollUntilRight(startingPoint);
|
|
165
|
+
else if ('scrollToLeft' === scrollToEventName) await page.scrollUntilLeft(startingPoint);
|
|
166
|
+
else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
|
|
167
|
+
else {
|
|
168
|
+
if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await page.scrollUp(param.distance || void 0, startingPoint);
|
|
169
|
+
else if ('left' === param.direction) await page.scrollLeft(param.distance || void 0, startingPoint);
|
|
170
|
+
else if ('right' === param.direction) await page.scrollRight(param.distance || void 0, startingPoint);
|
|
171
|
+
else throw new Error(`Unknown scroll direction: ${param.direction}`);
|
|
172
|
+
else await page.scrollDown(param?.distance || void 0, startingPoint);
|
|
173
|
+
await sleep(500);
|
|
174
|
+
}
|
|
175
|
+
}),
|
|
176
|
+
defineActionDragAndDrop(async (param)=>{
|
|
177
|
+
const from = param.from;
|
|
178
|
+
const to = param.to;
|
|
179
|
+
node_assert(from, 'missing "from" param for drag and drop');
|
|
180
|
+
node_assert(to, 'missing "to" param for drag and drop');
|
|
181
|
+
await page.mouse.drag({
|
|
182
|
+
x: from.center[0],
|
|
183
|
+
y: from.center[1]
|
|
184
|
+
}, {
|
|
185
|
+
x: to.center[0],
|
|
186
|
+
y: to.center[1]
|
|
187
|
+
});
|
|
188
|
+
}),
|
|
189
|
+
defineActionLongPress(async (param)=>{
|
|
190
|
+
const element = param.locate;
|
|
191
|
+
node_assert(element, 'Element not found, cannot long press');
|
|
192
|
+
const duration = param?.duration;
|
|
193
|
+
await page.longPress(element.center[0], element.center[1], duration);
|
|
194
|
+
}),
|
|
195
|
+
defineActionPinch(async (param)=>{
|
|
196
|
+
const { centerX, centerY, startDistance, endDistance, duration } = normalizePinchParam(param, await page.size());
|
|
197
|
+
await page.pinch(centerX, centerY, startDistance, endDistance, duration);
|
|
198
|
+
}),
|
|
199
|
+
...includeTouchEvents ? [
|
|
200
|
+
defineActionSwipe(async (param)=>{
|
|
201
|
+
const { width, height } = await page.size();
|
|
202
|
+
const { start, end } = param;
|
|
203
|
+
const startPoint = start ? {
|
|
204
|
+
x: start.center[0],
|
|
205
|
+
y: start.center[1]
|
|
206
|
+
} : {
|
|
207
|
+
x: width / 2,
|
|
208
|
+
y: height / 2
|
|
209
|
+
};
|
|
210
|
+
let endPoint;
|
|
211
|
+
if (end) endPoint = {
|
|
212
|
+
x: end.center[0],
|
|
213
|
+
y: end.center[1]
|
|
214
|
+
};
|
|
215
|
+
else if (param.distance) {
|
|
216
|
+
const direction = param.direction;
|
|
217
|
+
if (!direction) throw new Error('direction is required for swipe gesture');
|
|
218
|
+
endPoint = {
|
|
219
|
+
x: startPoint.x + ('right' === direction ? param.distance : 'left' === direction ? -param.distance : 0),
|
|
220
|
+
y: startPoint.y + ('down' === direction ? param.distance : 'up' === direction ? -param.distance : 0)
|
|
221
|
+
};
|
|
222
|
+
} else throw new Error('Either end or distance must be specified for swipe gesture');
|
|
223
|
+
endPoint.x = Math.max(0, Math.min(endPoint.x, width));
|
|
224
|
+
endPoint.y = Math.max(0, Math.min(endPoint.y, height));
|
|
225
|
+
const duration = param.duration;
|
|
226
|
+
debug(`swipe from ${startPoint.x}, ${startPoint.y} to ${endPoint.x}, ${endPoint.y} with duration ${duration}ms, repeat is set to ${param.repeat}`);
|
|
227
|
+
let repeat = 'number' == typeof param.repeat ? param.repeat : 1;
|
|
228
|
+
if (0 === repeat) repeat = 10;
|
|
229
|
+
for(let i = 0; i < repeat; i++)await page.swipe(startPoint, endPoint, duration);
|
|
230
|
+
})
|
|
231
|
+
] : [],
|
|
232
|
+
defineActionClearInput(async (param)=>{
|
|
233
|
+
await page.clearInput(param.locate);
|
|
234
|
+
}),
|
|
235
|
+
defineAction({
|
|
236
|
+
name: 'Navigate',
|
|
237
|
+
description: 'Navigate the browser to a specified URL. Opens the URL in the current tab.',
|
|
238
|
+
paramSchema: navigateParamSchema,
|
|
239
|
+
sample: {
|
|
240
|
+
url: 'https://www.example.com'
|
|
241
|
+
},
|
|
242
|
+
call: async (param)=>{
|
|
243
|
+
if (!page.navigate) throw new Error('Navigate operation is not supported on this page type');
|
|
244
|
+
await page.navigate(param.url);
|
|
245
|
+
}
|
|
246
|
+
}),
|
|
247
|
+
defineAction({
|
|
248
|
+
name: 'Reload',
|
|
249
|
+
description: 'Reload the current page',
|
|
250
|
+
call: async ()=>{
|
|
251
|
+
if (!page.reload) throw new Error('Reload operation is not supported on this page type');
|
|
252
|
+
await page.reload();
|
|
253
|
+
}
|
|
254
|
+
}),
|
|
255
|
+
defineAction({
|
|
256
|
+
name: 'GoBack',
|
|
257
|
+
description: 'Navigate back in browser history',
|
|
258
|
+
call: async ()=>{
|
|
259
|
+
if (!page.goBack) throw new Error('GoBack operation is not supported on this page type');
|
|
260
|
+
await page.goBack();
|
|
261
|
+
}
|
|
262
|
+
}),
|
|
263
|
+
defineAction({
|
|
264
|
+
name: 'GoForward',
|
|
265
|
+
description: 'Navigate forward in browser history',
|
|
266
|
+
call: async ()=>{
|
|
267
|
+
if (!page.goForward) throw new Error('GoForward operation is not supported on this page type');
|
|
268
|
+
await page.goForward();
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
];
|
|
272
|
+
export { AbstractWebPage, commonWebActionsForWebPage, getKeyCommands };
|
package/dist/lib/bin.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __webpack_exports__ = {};
|
|
3
|
+
const playground_namespaceObject = require("@godscene/playground");
|
|
4
|
+
require("dotenv/config");
|
|
5
|
+
const external_platform_js_namespaceObject = require("./platform.js");
|
|
6
|
+
async function startServer() {
|
|
7
|
+
const prepared = await external_platform_js_namespaceObject.webPlaygroundPlatform.prepare({
|
|
8
|
+
launchOptions: {
|
|
9
|
+
openBrowser: false,
|
|
10
|
+
verbose: false
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
const { server } = await (0, playground_namespaceObject.launchPreparedPlaygroundPlatform)(prepared);
|
|
14
|
+
console.log(`Midscene playground server is running on http://localhost:${server.port}`);
|
|
15
|
+
}
|
|
16
|
+
startServer().catch(console.error);
|
|
17
|
+
for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
18
|
+
Object.defineProperty(exports, '__esModule', {
|
|
19
|
+
value: true
|
|
20
|
+
});
|