@rpascene/web 0.30.8

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.
Files changed (135) hide show
  1. package/README.md +9 -0
  2. package/bin/midscene-playground +3 -0
  3. package/dist/es/bin.mjs +26 -0
  4. package/dist/es/bin.mjs.map +1 -0
  5. package/dist/es/bridge-mode/agent-cli-side.mjs +117 -0
  6. package/dist/es/bridge-mode/agent-cli-side.mjs.map +1 -0
  7. package/dist/es/bridge-mode/browser.mjs +2 -0
  8. package/dist/es/bridge-mode/common.mjs +37 -0
  9. package/dist/es/bridge-mode/common.mjs.map +1 -0
  10. package/dist/es/bridge-mode/index.mjs +4 -0
  11. package/dist/es/bridge-mode/io-client.mjs +101 -0
  12. package/dist/es/bridge-mode/io-client.mjs.map +1 -0
  13. package/dist/es/bridge-mode/io-server.mjs +208 -0
  14. package/dist/es/bridge-mode/io-server.mjs.map +1 -0
  15. package/dist/es/bridge-mode/page-browser-side.mjs +103 -0
  16. package/dist/es/bridge-mode/page-browser-side.mjs.map +1 -0
  17. package/dist/es/chrome-extension/agent.mjs +9 -0
  18. package/dist/es/chrome-extension/agent.mjs.map +1 -0
  19. package/dist/es/chrome-extension/cdpInput.mjs +174 -0
  20. package/dist/es/chrome-extension/cdpInput.mjs.LICENSE.txt +5 -0
  21. package/dist/es/chrome-extension/cdpInput.mjs.map +1 -0
  22. package/dist/es/chrome-extension/dynamic-scripts.mjs +37 -0
  23. package/dist/es/chrome-extension/dynamic-scripts.mjs.map +1 -0
  24. package/dist/es/chrome-extension/index.mjs +5 -0
  25. package/dist/es/chrome-extension/page.mjs +633 -0
  26. package/dist/es/chrome-extension/page.mjs.map +1 -0
  27. package/dist/es/index.mjs +6 -0
  28. package/dist/es/playwright/ai-fixture.mjs +325 -0
  29. package/dist/es/playwright/ai-fixture.mjs.map +1 -0
  30. package/dist/es/playwright/index.mjs +23 -0
  31. package/dist/es/playwright/index.mjs.map +1 -0
  32. package/dist/es/playwright/page.mjs +9 -0
  33. package/dist/es/playwright/page.mjs.map +1 -0
  34. package/dist/es/playwright/reporter/index.mjs +90 -0
  35. package/dist/es/playwright/reporter/index.mjs.map +1 -0
  36. package/dist/es/puppeteer/agent-launcher.mjs +132 -0
  37. package/dist/es/puppeteer/agent-launcher.mjs.map +1 -0
  38. package/dist/es/puppeteer/base-page.mjs +491 -0
  39. package/dist/es/puppeteer/base-page.mjs.map +1 -0
  40. package/dist/es/puppeteer/index.mjs +17 -0
  41. package/dist/es/puppeteer/index.mjs.map +1 -0
  42. package/dist/es/puppeteer/page.mjs +9 -0
  43. package/dist/es/puppeteer/page.mjs.map +1 -0
  44. package/dist/es/static/index.mjs +3 -0
  45. package/dist/es/static/static-agent.mjs +10 -0
  46. package/dist/es/static/static-agent.mjs.map +1 -0
  47. package/dist/es/static/static-page.mjs +132 -0
  48. package/dist/es/static/static-page.mjs.map +1 -0
  49. package/dist/es/web-element.mjs +96 -0
  50. package/dist/es/web-element.mjs.map +1 -0
  51. package/dist/es/web-page.mjs +206 -0
  52. package/dist/es/web-page.mjs.map +1 -0
  53. package/dist/lib/bin.js +54 -0
  54. package/dist/lib/bin.js.map +1 -0
  55. package/dist/lib/bridge-mode/agent-cli-side.js +154 -0
  56. package/dist/lib/bridge-mode/agent-cli-side.js.map +1 -0
  57. package/dist/lib/bridge-mode/browser.js +38 -0
  58. package/dist/lib/bridge-mode/browser.js.map +1 -0
  59. package/dist/lib/bridge-mode/common.js +95 -0
  60. package/dist/lib/bridge-mode/common.js.map +1 -0
  61. package/dist/lib/bridge-mode/index.js +46 -0
  62. package/dist/lib/bridge-mode/index.js.map +1 -0
  63. package/dist/lib/bridge-mode/io-client.js +135 -0
  64. package/dist/lib/bridge-mode/io-client.js.map +1 -0
  65. package/dist/lib/bridge-mode/io-server.js +245 -0
  66. package/dist/lib/bridge-mode/io-server.js.map +1 -0
  67. package/dist/lib/bridge-mode/page-browser-side.js +147 -0
  68. package/dist/lib/bridge-mode/page-browser-side.js.map +1 -0
  69. package/dist/lib/chrome-extension/agent.js +43 -0
  70. package/dist/lib/chrome-extension/agent.js.map +1 -0
  71. package/dist/lib/chrome-extension/cdpInput.js +208 -0
  72. package/dist/lib/chrome-extension/cdpInput.js.LICENSE.txt +5 -0
  73. package/dist/lib/chrome-extension/cdpInput.js.map +1 -0
  74. package/dist/lib/chrome-extension/dynamic-scripts.js +77 -0
  75. package/dist/lib/chrome-extension/dynamic-scripts.js.map +1 -0
  76. package/dist/lib/chrome-extension/index.js +60 -0
  77. package/dist/lib/chrome-extension/index.js.map +1 -0
  78. package/dist/lib/chrome-extension/page.js +667 -0
  79. package/dist/lib/chrome-extension/page.js.map +1 -0
  80. package/dist/lib/index.js +60 -0
  81. package/dist/lib/index.js.map +1 -0
  82. package/dist/lib/playwright/ai-fixture.js +362 -0
  83. package/dist/lib/playwright/ai-fixture.js.map +1 -0
  84. package/dist/lib/playwright/index.js +66 -0
  85. package/dist/lib/playwright/index.js.map +1 -0
  86. package/dist/lib/playwright/page.js +43 -0
  87. package/dist/lib/playwright/page.js.map +1 -0
  88. package/dist/lib/playwright/reporter/index.js +124 -0
  89. package/dist/lib/playwright/reporter/index.js.map +1 -0
  90. package/dist/lib/puppeteer/agent-launcher.js +194 -0
  91. package/dist/lib/puppeteer/agent-launcher.js.map +1 -0
  92. package/dist/lib/puppeteer/base-page.js +531 -0
  93. package/dist/lib/puppeteer/base-page.js.map +1 -0
  94. package/dist/lib/puppeteer/index.js +57 -0
  95. package/dist/lib/puppeteer/index.js.map +1 -0
  96. package/dist/lib/puppeteer/page.js +43 -0
  97. package/dist/lib/puppeteer/page.js.map +1 -0
  98. package/dist/lib/static/index.js +52 -0
  99. package/dist/lib/static/index.js.map +1 -0
  100. package/dist/lib/static/static-agent.js +44 -0
  101. package/dist/lib/static/static-agent.js.map +1 -0
  102. package/dist/lib/static/static-page.js +166 -0
  103. package/dist/lib/static/static-page.js.map +1 -0
  104. package/dist/lib/web-element.js +136 -0
  105. package/dist/lib/web-element.js.map +1 -0
  106. package/dist/lib/web-page.js +256 -0
  107. package/dist/lib/web-page.js.map +1 -0
  108. package/dist/types/bin.d.ts +1 -0
  109. package/dist/types/bridge-mode/agent-cli-side.d.ts +32 -0
  110. package/dist/types/bridge-mode/browser.d.ts +2 -0
  111. package/dist/types/bridge-mode/common.d.ts +60 -0
  112. package/dist/types/bridge-mode/index.d.ts +4 -0
  113. package/dist/types/bridge-mode/io-client.d.ts +10 -0
  114. package/dist/types/bridge-mode/io-server.d.ts +26 -0
  115. package/dist/types/bridge-mode/page-browser-side.d.ts +18 -0
  116. package/dist/types/chrome-extension/agent.d.ts +5 -0
  117. package/dist/types/chrome-extension/cdpInput.d.ts +52 -0
  118. package/dist/types/chrome-extension/dynamic-scripts.d.ts +3 -0
  119. package/dist/types/chrome-extension/index.d.ts +5 -0
  120. package/dist/types/chrome-extension/page.d.ts +95 -0
  121. package/dist/types/index.d.ts +9 -0
  122. package/dist/types/playwright/ai-fixture.d.ts +117 -0
  123. package/dist/types/playwright/index.d.ts +12 -0
  124. package/dist/types/playwright/page.d.ts +6 -0
  125. package/dist/types/playwright/reporter/index.d.ts +18 -0
  126. package/dist/types/puppeteer/agent-launcher.d.ts +29 -0
  127. package/dist/types/puppeteer/base-page.d.ts +90 -0
  128. package/dist/types/puppeteer/index.d.ts +9 -0
  129. package/dist/types/puppeteer/page.d.ts +6 -0
  130. package/dist/types/static/index.d.ts +2 -0
  131. package/dist/types/static/static-agent.d.ts +5 -0
  132. package/dist/types/static/static-page.d.ts +52 -0
  133. package/dist/types/web-element.d.ts +51 -0
  134. package/dist/types/web-page.d.ts +59 -0
  135. package/package.json +164 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"playwright\\reporter\\index.mjs","sources":["webpack://@rpascene/web/./src/playwright/reporter/index.ts"],"sourcesContent":["import { readFileSync, rmSync } from 'node:fs';\nimport type { ReportDumpWithAttributes } from '@rpascene/core';\nimport { getReportFileName, printReportMsg } from '@rpascene/core/agent';\nimport { writeDumpReport } from '@rpascene/core/utils';\nimport { replaceIllegalPathCharsAndSpace } from '@rpascene/shared/utils';\nimport type {\n FullConfig,\n Reporter,\n Suite,\n TestCase,\n TestResult,\n} from '@playwright/test/reporter';\n\ninterface RpasceneReporterOptions {\n type?: 'merged' | 'separate';\n}\n\nclass RpasceneReporter implements Reporter {\n private mergedFilename?: string;\n private testTitleToFilename = new Map<string, string>();\n mode?: 'merged' | 'separate';\n\n constructor(options: RpasceneReporterOptions = {}) {\n // Set mode from constructor options (official Playwright way)\n this.mode = RpasceneReporter.getMode(options.type ?? 'merged');\n }\n\n private static getMode(reporterType: string): 'merged' | 'separate' {\n if (!reporterType) {\n return 'merged';\n }\n if (reporterType !== 'merged' && reporterType !== 'separate') {\n throw new Error(\n `Unknown reporter type in playwright config: ${reporterType}, only support 'merged' or 'separate'`,\n );\n }\n return reporterType;\n }\n\n private getSeparatedFilename(testTitle: string): string {\n if (!this.testTitleToFilename.has(testTitle)) {\n const baseTag = `playwright-${replaceIllegalPathCharsAndSpace(testTitle)}`;\n const generatedFilename = getReportFileName(baseTag);\n this.testTitleToFilename.set(testTitle, generatedFilename);\n }\n return this.testTitleToFilename.get(testTitle)!;\n }\n\n private getReportFilename(testTitle?: string): string {\n if (this.mode === 'merged') {\n if (!this.mergedFilename) {\n this.mergedFilename = getReportFileName('playwright-merged');\n }\n return this.mergedFilename;\n } else if (this.mode === 'separate') {\n if (!testTitle) throw new Error('testTitle is required in separate mode');\n return this.getSeparatedFilename(testTitle);\n }\n throw new Error(`Unknown mode: ${this.mode}`);\n }\n\n private updateReport(testData: ReportDumpWithAttributes) {\n if (!testData || !this.mode) return;\n const fileName = this.getReportFilename(\n testData.attributes?.playwright_test_title,\n );\n const reportPath = writeDumpReport(\n fileName,\n testData,\n this.mode === 'merged',\n );\n reportPath && printReportMsg(reportPath);\n }\n\n async onBegin(config: FullConfig, suite: Suite) { }\n\n onTestBegin(_test: TestCase, _result: TestResult) {\n // logger(`Starting test ${test.title}`);\n }\n\n onTestEnd(test: TestCase, result: TestResult) {\n const dumpAnnotation = test.annotations.find((annotation) => {\n return annotation.type === 'RPASCENE_DUMP_ANNOTATION';\n });\n if (!dumpAnnotation?.description) return;\n\n const tempFilePath = dumpAnnotation.description;\n let dumpString: string;\n\n try {\n dumpString = readFileSync(tempFilePath, 'utf-8');\n } catch (error) {\n console.error(\n `Failed to read Rpascene dump file: ${tempFilePath}`,\n error,\n );\n return;\n }\n\n const retry = result.retry ? `(retry #${result.retry})` : '';\n const testId = `${test.id}${retry}`;\n const testData: ReportDumpWithAttributes = {\n dumpString,\n attributes: {\n playwright_test_id: testId,\n playwright_test_title: `${test.title}${retry}`,\n playwright_test_status: result.status,\n playwright_test_duration: result.duration,\n },\n };\n\n this.updateReport(testData);\n\n // Clean up: delete temp file\n try {\n rmSync(tempFilePath, { force: true });\n } catch (error) {\n console.warn(\n `Failed to delete Rpascene temp file: ${tempFilePath}`,\n error,\n );\n }\n }\n}\n\nexport default RpasceneReporter;\n"],"names":["RpasceneReporter","reporterType","Error","testTitle","baseTag","replaceIllegalPathCharsAndSpace","generatedFilename","getReportFileName","testData","_testData_attributes","fileName","reportPath","writeDumpReport","printReportMsg","config","suite","_test","_result","test","result","dumpAnnotation","annotation","tempFilePath","dumpString","readFileSync","error","console","retry","testId","rmSync","options","Map"],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { getDebug } from "@rpascene/shared/logger";
3
+ import { assert } from "@rpascene/shared/utils";
4
+ import { PuppeteerAgent } from "./index.mjs";
5
+ import { DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT } from "@rpascene/shared/constants";
6
+ import puppeteer from "puppeteer";
7
+ const defaultUA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36';
8
+ const defaultViewportWidth = 1440;
9
+ const defaultViewportHeight = 768;
10
+ const defaultViewportScale = 'darwin' === process.platform ? 2 : 1;
11
+ const defaultWaitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT;
12
+ const launcherDebug = getDebug('puppeteer:launcher');
13
+ async function launchPuppeteerPage(target, preference, browser) {
14
+ var _target_waitForNetworkIdle;
15
+ assert(target.url, 'url is required');
16
+ const freeFn = [];
17
+ const ua = target.userAgent || defaultUA;
18
+ let width = defaultViewportWidth;
19
+ let preferMaximizedWindow = true;
20
+ if (target.viewportWidth) {
21
+ preferMaximizedWindow = false;
22
+ assert('number' == typeof target.viewportWidth, 'viewportWidth must be a number');
23
+ width = Number.parseInt(target.viewportWidth, 10);
24
+ assert(width > 0, `viewportWidth must be greater than 0, but got ${width}`);
25
+ }
26
+ let height = defaultViewportHeight;
27
+ if (target.viewportHeight) {
28
+ preferMaximizedWindow = false;
29
+ assert('number' == typeof target.viewportHeight, 'viewportHeight must be a number');
30
+ height = Number.parseInt(target.viewportHeight, 10);
31
+ assert(height > 0, `viewportHeight must be greater than 0, but got ${height}`);
32
+ }
33
+ let dpr = defaultViewportScale;
34
+ if (target.viewportScale) {
35
+ preferMaximizedWindow = false;
36
+ assert('number' == typeof target.viewportScale, 'viewportScale must be a number');
37
+ dpr = Number.parseInt(target.viewportScale, 10);
38
+ assert(dpr > 0, `viewportScale must be greater than 0, but got ${dpr}`);
39
+ }
40
+ const viewportConfig = {
41
+ width,
42
+ height,
43
+ deviceScaleFactor: dpr
44
+ };
45
+ const headed = (null == preference ? void 0 : preference.headed) || (null == preference ? void 0 : preference.keepWindow);
46
+ preferMaximizedWindow = preferMaximizedWindow && !!headed;
47
+ if (headed && '1' === process.env.CI) console.warn('you are probably running headed mode in CI, this will usually fail.');
48
+ const isWindows = 'win32' === process.platform;
49
+ const args = [
50
+ ...isWindows ? [] : [
51
+ '--no-sandbox',
52
+ '--disable-setuid-sandbox'
53
+ ],
54
+ '--disable-features=HttpsFirstBalancedModeAutoEnable',
55
+ '--disable-features=PasswordLeakDetection',
56
+ '--disable-save-password-bubble',
57
+ `--user-agent="${ua}"`,
58
+ preferMaximizedWindow ? '--start-maximized' : `--window-size=${width},${height + 200}`
59
+ ];
60
+ launcherDebug('launching browser with viewport, headed', headed, 'viewport', viewportConfig, 'args', args, 'preference', preference);
61
+ let browserInstance = browser;
62
+ if (!browserInstance) {
63
+ browserInstance = await puppeteer.launch({
64
+ headless: !(null == preference ? void 0 : preference.headed),
65
+ defaultViewport: viewportConfig,
66
+ args,
67
+ acceptInsecureCerts: target.acceptInsecureCerts
68
+ });
69
+ freeFn.push({
70
+ name: 'puppeteer_browser',
71
+ fn: ()=>{
72
+ if (!(null == preference ? void 0 : preference.keepWindow)) if (isWindows) setTimeout(()=>{
73
+ null == browserInstance || browserInstance.close();
74
+ }, 800);
75
+ else null == browserInstance || browserInstance.close();
76
+ }
77
+ });
78
+ }
79
+ const page = await browserInstance.newPage();
80
+ if (target.cookie) {
81
+ const cookieFileContent = readFileSync(target.cookie, 'utf-8');
82
+ await browserInstance.setCookie(...JSON.parse(cookieFileContent));
83
+ }
84
+ if (ua) await page.setUserAgent(ua);
85
+ if (viewportConfig) await page.setViewport(viewportConfig);
86
+ const waitForNetworkIdleTimeout = 'number' == typeof (null == (_target_waitForNetworkIdle = target.waitForNetworkIdle) ? void 0 : _target_waitForNetworkIdle.timeout) ? target.waitForNetworkIdle.timeout : defaultWaitForNetworkIdleTimeout;
87
+ try {
88
+ launcherDebug('goto', target.url);
89
+ await page.goto(target.url);
90
+ if (waitForNetworkIdleTimeout > 0) {
91
+ launcherDebug('waitForNetworkIdle', waitForNetworkIdleTimeout);
92
+ await page.waitForNetworkIdle({
93
+ timeout: waitForNetworkIdleTimeout
94
+ });
95
+ }
96
+ } catch (e) {
97
+ var _target_waitForNetworkIdle1, _target_waitForNetworkIdle2;
98
+ if ('boolean' == typeof (null == (_target_waitForNetworkIdle1 = target.waitForNetworkIdle) ? void 0 : _target_waitForNetworkIdle1.continueOnNetworkIdleError) && !(null == (_target_waitForNetworkIdle2 = target.waitForNetworkIdle) ? void 0 : _target_waitForNetworkIdle2.continueOnNetworkIdleError)) {
99
+ const newError = new Error(`failed to wait for network idle: ${e}`, {
100
+ cause: e
101
+ });
102
+ throw newError;
103
+ }
104
+ const newMessage = `failed to wait for network idle after ${waitForNetworkIdleTimeout}ms, but the script will continue.`;
105
+ console.warn(newMessage);
106
+ }
107
+ return {
108
+ page,
109
+ freeFn
110
+ };
111
+ }
112
+ async function puppeteerAgentForTarget(target, preference, browser) {
113
+ const { page, freeFn } = await launchPuppeteerPage(target, preference, browser);
114
+ const agent = new PuppeteerAgent(page, {
115
+ autoPrintReportMsg: false,
116
+ testId: null == preference ? void 0 : preference.testId,
117
+ cache: null == preference ? void 0 : preference.cache,
118
+ aiActionContext: target.aiActionContext,
119
+ forceSameTabNavigation: void 0 !== target.forceSameTabNavigation ? target.forceSameTabNavigation : true
120
+ });
121
+ freeFn.push({
122
+ name: 'rpascene_puppeteer_agent',
123
+ fn: ()=>agent.destroy()
124
+ });
125
+ return {
126
+ agent,
127
+ freeFn
128
+ };
129
+ }
130
+ export { defaultUA, defaultViewportHeight, defaultViewportScale, defaultViewportWidth, defaultWaitForNetworkIdleTimeout, launchPuppeteerPage, puppeteerAgentForTarget };
131
+
132
+ //# sourceMappingURL=agent-launcher.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"puppeteer\\agent-launcher.mjs","sources":["webpack://@rpascene/web/./src/puppeteer/agent-launcher.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { getDebug } from '@rpascene/shared/logger';\nimport { assert } from '@rpascene/shared/utils';\n\nimport { PuppeteerAgent } from '@/puppeteer/index';\nimport type { Cache, RpasceneYamlScriptWebEnv } from '@rpascene/core';\nimport { DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT } from '@rpascene/shared/constants';\nimport puppeteer, { type Browser } from 'puppeteer';\n\nexport const defaultUA =\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36';\nexport const defaultViewportWidth = 1440;\nexport const defaultViewportHeight = 768;\nexport const defaultViewportScale = process.platform === 'darwin' ? 2 : 1;\nexport const defaultWaitForNetworkIdleTimeout =\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT;\n\ninterface FreeFn {\n name: string;\n fn: () => void;\n}\n\nconst launcherDebug = getDebug('puppeteer:launcher');\n\nexport async function launchPuppeteerPage(\n target: RpasceneYamlScriptWebEnv,\n preference?: {\n headed?: boolean;\n keepWindow?: boolean;\n },\n browser?: Browser,\n) {\n assert(target.url, 'url is required');\n const freeFn: FreeFn[] = [];\n\n // prepare the environment\n const ua = target.userAgent || defaultUA;\n let width = defaultViewportWidth;\n let preferMaximizedWindow = true;\n if (target.viewportWidth) {\n preferMaximizedWindow = false;\n assert(\n typeof target.viewportWidth === 'number',\n 'viewportWidth must be a number',\n );\n width = Number.parseInt(target.viewportWidth as unknown as string, 10);\n assert(width > 0, `viewportWidth must be greater than 0, but got ${width}`);\n }\n let height = defaultViewportHeight;\n if (target.viewportHeight) {\n preferMaximizedWindow = false;\n assert(\n typeof target.viewportHeight === 'number',\n 'viewportHeight must be a number',\n );\n height = Number.parseInt(target.viewportHeight as unknown as string, 10);\n assert(\n height > 0,\n `viewportHeight must be greater than 0, but got ${height}`,\n );\n }\n let dpr = defaultViewportScale;\n if (target.viewportScale) {\n preferMaximizedWindow = false;\n assert(\n typeof target.viewportScale === 'number',\n 'viewportScale must be a number',\n );\n dpr = Number.parseInt(target.viewportScale as unknown as string, 10);\n assert(dpr > 0, `viewportScale must be greater than 0, but got ${dpr}`);\n }\n const viewportConfig = {\n width,\n height,\n deviceScaleFactor: dpr,\n };\n\n const headed = preference?.headed || preference?.keepWindow;\n\n // only maximize window in headed mode\n preferMaximizedWindow = preferMaximizedWindow && !!headed;\n\n // launch the browser\n if (headed && process.env.CI === '1') {\n console.warn(\n 'you are probably running headed mode in CI, this will usually fail.',\n );\n }\n // do not use 'no-sandbox' on windows https://www.perplexity.ai/search/how-to-solve-this-with-nodejs-dMHpdCypRa..JA8TkQzbeQ\n const isWindows = process.platform === 'win32';\n const args = [\n ...(isWindows ? [] : ['--no-sandbox', '--disable-setuid-sandbox']),\n '--disable-features=HttpsFirstBalancedModeAutoEnable',\n '--disable-features=PasswordLeakDetection',\n '--disable-save-password-bubble',\n `--user-agent=\"${ua}\"`,\n preferMaximizedWindow\n ? '--start-maximized'\n : `--window-size=${width},${height + 200}`, // add 200px for the address bar\n ];\n\n launcherDebug(\n 'launching browser with viewport, headed',\n headed,\n 'viewport',\n viewportConfig,\n 'args',\n args,\n 'preference',\n preference,\n );\n let browserInstance = browser;\n if (!browserInstance) {\n browserInstance = await puppeteer.launch({\n headless: !preference?.headed,\n defaultViewport: viewportConfig,\n args,\n acceptInsecureCerts: target.acceptInsecureCerts,\n });\n freeFn.push({\n name: 'puppeteer_browser',\n fn: () => {\n if (!preference?.keepWindow) {\n if (isWindows) {\n setTimeout(() => {\n browserInstance?.close();\n }, 800);\n } else {\n browserInstance?.close();\n }\n }\n },\n });\n }\n const page = await browserInstance.newPage();\n // await page.setUserAgent(ua);\n // await page.setViewport(viewportConfig);\n\n if (target.cookie) {\n const cookieFileContent = readFileSync(target.cookie, 'utf-8');\n await browserInstance.setCookie(...JSON.parse(cookieFileContent));\n }\n\n if (ua) {\n await page.setUserAgent(ua);\n }\n\n if (viewportConfig) {\n await page.setViewport(viewportConfig);\n }\n\n const waitForNetworkIdleTimeout =\n typeof target.waitForNetworkIdle?.timeout === 'number'\n ? target.waitForNetworkIdle.timeout\n : defaultWaitForNetworkIdleTimeout;\n\n try {\n launcherDebug('goto', target.url);\n await page.goto(target.url);\n if (waitForNetworkIdleTimeout > 0) {\n launcherDebug('waitForNetworkIdle', waitForNetworkIdleTimeout);\n await page.waitForNetworkIdle({\n timeout: waitForNetworkIdleTimeout,\n });\n }\n } catch (e) {\n if (\n typeof target.waitForNetworkIdle?.continueOnNetworkIdleError ===\n 'boolean' &&\n !target.waitForNetworkIdle?.continueOnNetworkIdleError\n ) {\n const newError = new Error(`failed to wait for network idle: ${e}`, {\n cause: e,\n });\n throw newError;\n }\n const newMessage = `failed to wait for network idle after ${waitForNetworkIdleTimeout}ms, but the script will continue.`;\n console.warn(newMessage);\n }\n\n return { page, freeFn };\n}\n\nexport async function puppeteerAgentForTarget(\n target: RpasceneYamlScriptWebEnv,\n preference?: {\n headed?: boolean;\n keepWindow?: boolean;\n testId?: string;\n cache?: Cache;\n },\n browser?: Browser,\n) {\n const { page, freeFn } = await launchPuppeteerPage(\n target,\n preference,\n browser,\n );\n\n // prepare Rpascene agent\n const agent = new PuppeteerAgent(page, {\n autoPrintReportMsg: false,\n testId: preference?.testId,\n cache: preference?.cache,\n aiActionContext: target.aiActionContext,\n forceSameTabNavigation:\n typeof target.forceSameTabNavigation !== 'undefined'\n ? target.forceSameTabNavigation\n : true, // true for default in yaml script\n });\n\n freeFn.push({\n name: 'rpascene_puppeteer_agent',\n fn: () => agent.destroy(),\n });\n\n return { agent, freeFn };\n}\n"],"names":["defaultUA","defaultViewportWidth","defaultViewportHeight","defaultViewportScale","process","defaultWaitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","launcherDebug","getDebug","launchPuppeteerPage","target","preference","browser","_target_waitForNetworkIdle","assert","freeFn","ua","width","preferMaximizedWindow","Number","height","dpr","viewportConfig","headed","console","isWindows","args","browserInstance","puppeteer","setTimeout","page","cookieFileContent","readFileSync","JSON","waitForNetworkIdleTimeout","e","_target_waitForNetworkIdle1","_target_waitForNetworkIdle2","newError","Error","newMessage","puppeteerAgentForTarget","agent","PuppeteerAgent"],"mappings":"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"}
@@ -0,0 +1,491 @@
1
+ import { WebPageContextParser } from "../web-element.mjs";
2
+ import { sleep } from "@rpascene/core/utils";
3
+ import { DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY, DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT } from "@rpascene/shared/constants";
4
+ import { treeToList } from "@rpascene/shared/extractor";
5
+ import { createImgBase64ByFormat } from "@rpascene/shared/img";
6
+ import { getDebug } from "@rpascene/shared/logger";
7
+ import { getElementInfosScriptContent, getExtraReturnLogic } from "@rpascene/shared/node";
8
+ import { assert } from "@rpascene/shared/utils";
9
+ import { commonWebActionsForWebPage } from "../web-page.mjs";
10
+ function _define_property(obj, key, value) {
11
+ if (key in obj) Object.defineProperty(obj, key, {
12
+ value: value,
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true
16
+ });
17
+ else obj[key] = value;
18
+ return obj;
19
+ }
20
+ const debugPage = getDebug('web:page');
21
+ const sanitizeXpaths = (xpaths)=>{
22
+ if (!Array.isArray(xpaths)) return [];
23
+ return xpaths.filter((xpath)=>'string' == typeof xpath && xpath.length > 0);
24
+ };
25
+ class Page {
26
+ actionSpace() {
27
+ const defaultActions = commonWebActionsForWebPage(this);
28
+ const customActions = this.customActions || [];
29
+ return [
30
+ ...defaultActions,
31
+ ...customActions
32
+ ];
33
+ }
34
+ async evaluate(pageFunction, arg) {
35
+ let result;
36
+ debugPage('evaluate function begin');
37
+ this.interfaceType, result = await this.underlyingPage.evaluate(pageFunction, arg);
38
+ debugPage('evaluate function end');
39
+ return result;
40
+ }
41
+ async evaluateJavaScript(script) {
42
+ return this.evaluate(script);
43
+ }
44
+ async waitForNavigation() {
45
+ if (0 === this.waitForNavigationTimeout) return void debugPage('waitForNavigation timeout is 0, skip waiting');
46
+ if ('puppeteer' === this.interfaceType || 'playwright' === this.interfaceType) {
47
+ debugPage('waitForNavigation begin');
48
+ debugPage(`waitForNavigation timeout: ${this.waitForNavigationTimeout}`);
49
+ try {
50
+ await this.underlyingPage.waitForSelector('html', {
51
+ timeout: this.waitForNavigationTimeout
52
+ });
53
+ } catch (error) {
54
+ console.warn('[rpascene:warning] Waiting for the "navigation" has timed out, but Rpascene will continue execution. Please check https://rpascenejs.com/faq.html#customize-the-network-timeout for more information on customizing the network timeout');
55
+ }
56
+ debugPage('waitForNavigation end');
57
+ }
58
+ }
59
+ async waitForNetworkIdle() {
60
+ if ('puppeteer' === this.interfaceType) {
61
+ if (0 === this.waitForNetworkIdleTimeout) return void debugPage('waitForNetworkIdle timeout is 0, skip waiting');
62
+ try {
63
+ await this.underlyingPage.waitForNetworkIdle({
64
+ idleTime: 200,
65
+ concurrency: DEFAULT_WAIT_FOR_NETWORK_IDLE_CONCURRENCY,
66
+ timeout: this.waitForNetworkIdleTimeout
67
+ });
68
+ } catch (error) {
69
+ console.warn('[rpascene:warning] Waiting for the "network idle" has timed out, but Rpascene will continue execution. Please check https://rpascenejs.com/faq.html#customize-the-network-timeout for more information on customizing the network timeout');
70
+ }
71
+ }
72
+ }
73
+ async getElementsInfo() {
74
+ await this.waitForNavigation();
75
+ debugPage('getElementsInfo begin');
76
+ const tree = await this.getElementsNodeTree();
77
+ debugPage('getElementsInfo end');
78
+ return treeToList(tree);
79
+ }
80
+ async getXpathsById(id) {
81
+ const elementInfosScriptContent = getElementInfosScriptContent();
82
+ return this.evaluateJavaScript(`${elementInfosScriptContent}rpascene_element_inspector.getXpathsById(${JSON.stringify(id)})`);
83
+ }
84
+ async getXpathsByPoint(point, isOrderSensitive) {
85
+ const elementInfosScriptContent = getElementInfosScriptContent();
86
+ return this.evaluateJavaScript(`${elementInfosScriptContent}rpascene_element_inspector.getXpathsByPoint({left: ${point.left}, top: ${point.top}}, ${isOrderSensitive})`);
87
+ }
88
+ async getElementInfoByXpath(xpath) {
89
+ const elementInfosScriptContent = getElementInfosScriptContent();
90
+ return this.evaluateJavaScript(`${elementInfosScriptContent}rpascene_element_inspector.getElementInfoByXpath(${JSON.stringify(xpath)})`);
91
+ }
92
+ async cacheFeatureForRect(rect, opt) {
93
+ const center = {
94
+ left: Math.floor(rect.left + rect.width / 2),
95
+ top: Math.floor(rect.top + rect.height / 2)
96
+ };
97
+ try {
98
+ const orderSensitive = (null == opt ? void 0 : opt._orderSensitive) ?? false;
99
+ const xpaths = await this.getXpathsByPoint(center, orderSensitive);
100
+ const sanitized = sanitizeXpaths(xpaths);
101
+ if (!sanitized.length) debugPage('cacheFeatureForRect: no xpath found at rect %o', rect);
102
+ return {
103
+ xpaths: sanitized
104
+ };
105
+ } catch (error) {
106
+ debugPage('cacheFeatureForRect failed: %s', error);
107
+ return {
108
+ xpaths: []
109
+ };
110
+ }
111
+ }
112
+ async rectMatchesCacheFeature(feature) {
113
+ const webFeature = feature;
114
+ const xpaths = sanitizeXpaths(webFeature.xpaths);
115
+ for (const xpath of xpaths)try {
116
+ const elementInfo = await this.getElementInfoByXpath(xpath);
117
+ if (null == elementInfo ? void 0 : elementInfo.rect) {
118
+ var _this_viewportSize;
119
+ const matchedRect = {
120
+ left: elementInfo.rect.left,
121
+ top: elementInfo.rect.top,
122
+ width: elementInfo.rect.width,
123
+ height: elementInfo.rect.height
124
+ };
125
+ if (null == (_this_viewportSize = this.viewportSize) ? void 0 : _this_viewportSize.dpr) matchedRect.dpr = this.viewportSize.dpr;
126
+ return matchedRect;
127
+ }
128
+ } catch (error) {
129
+ debugPage('rectMatchesCacheFeature failed for xpath %s: %s', xpath, error);
130
+ }
131
+ throw new Error('No matching element rect found for the provided cache feature');
132
+ }
133
+ async getElementsNodeTree() {
134
+ await this.waitForNavigation();
135
+ const scriptContent = getElementInfosScriptContent();
136
+ const frames = this.underlyingPage.frames();
137
+ for (let frame of frames)await frame.evaluate(scriptContent);
138
+ const scripts = await getExtraReturnLogic(true);
139
+ assert(scripts, "scripts should be set before writing report in browser");
140
+ const startTime = Date.now();
141
+ const captureElementSnapshot = await this.evaluate(scripts);
142
+ const endTime = Date.now();
143
+ debugPage(`getElementsNodeTree end, cost: ${endTime - startTime}ms`);
144
+ const sizeInfo = await this.evaluate(()=>({
145
+ width: document.documentElement.clientWidth,
146
+ height: document.documentElement.clientHeight,
147
+ dpr: window.devicePixelRatio
148
+ }));
149
+ this.viewportSize = sizeInfo;
150
+ return captureElementSnapshot;
151
+ }
152
+ async size() {
153
+ const sizeInfo = await this.evaluate(()=>({
154
+ width: document.documentElement.clientWidth,
155
+ height: document.documentElement.clientHeight,
156
+ dpr: window.devicePixelRatio
157
+ }));
158
+ this.viewportSize = sizeInfo;
159
+ return sizeInfo;
160
+ }
161
+ async screenshotBase64() {
162
+ const imgType = 'jpeg';
163
+ const quality = 90;
164
+ await this.waitForNavigation();
165
+ const startTime = Date.now();
166
+ debugPage('screenshotBase64 begin');
167
+ let base64;
168
+ if ('puppeteer' === this.interfaceType) {
169
+ const result = await this.underlyingPage.screenshot({
170
+ type: imgType,
171
+ quality,
172
+ encoding: 'base64'
173
+ });
174
+ base64 = createImgBase64ByFormat(imgType, result);
175
+ } else if ('playwright' === this.interfaceType) {
176
+ const buffer = await this.underlyingPage.screenshot({
177
+ type: imgType,
178
+ quality,
179
+ timeout: 10000
180
+ });
181
+ base64 = createImgBase64ByFormat(imgType, buffer.toString('base64'));
182
+ } else throw new Error('Unsupported page type for screenshot');
183
+ const endTime = Date.now();
184
+ debugPage(`screenshotBase64 end, cost: ${endTime - startTime}ms`);
185
+ return base64;
186
+ }
187
+ async url() {
188
+ return this.underlyingPage.url();
189
+ }
190
+ describe() {
191
+ const url = this.underlyingPage.url();
192
+ return url || '';
193
+ }
194
+ get mouse() {
195
+ return {
196
+ click: async (x, y, options)=>{
197
+ await this.mouse.move(x, y);
198
+ const { button = 'left', count = 1 } = options || {};
199
+ debugPage(`mouse click ${x}, ${y}, ${button}, ${count}`);
200
+ if (2 === count && 'playwright' === this.interfaceType) await this.underlyingPage.mouse.dblclick(x, y, {
201
+ button
202
+ });
203
+ else if ('puppeteer' === this.interfaceType) if ('left' === button && 1 === count) await this.underlyingPage.mouse.click(x, y);
204
+ else await this.underlyingPage.mouse.click(x, y, {
205
+ button,
206
+ count
207
+ });
208
+ else if ('playwright' === this.interfaceType) this.underlyingPage.mouse.click(x, y, {
209
+ button,
210
+ clickCount: count
211
+ });
212
+ },
213
+ wheel: async (deltaX, deltaY)=>{
214
+ debugPage(`mouse wheel ${deltaX}, ${deltaY}`);
215
+ if ('puppeteer' === this.interfaceType) await this.underlyingPage.mouse.wheel({
216
+ deltaX,
217
+ deltaY
218
+ });
219
+ else if ('playwright' === this.interfaceType) await this.underlyingPage.mouse.wheel(deltaX, deltaY);
220
+ },
221
+ move: async (x, y)=>{
222
+ this.everMoved = true;
223
+ debugPage(`mouse move to ${x}, ${y}`);
224
+ return this.underlyingPage.mouse.move(x, y);
225
+ },
226
+ drag: async (from, to)=>{
227
+ debugPage(`begin mouse drag from ${from.x}, ${from.y} to ${to.x}, ${to.y}`);
228
+ await this.underlyingPage.mouse.move(from.x, from.y);
229
+ await sleep(200);
230
+ await this.underlyingPage.mouse.down();
231
+ await sleep(300);
232
+ await this.underlyingPage.mouse.move(to.x, to.y);
233
+ await sleep(500);
234
+ await this.underlyingPage.mouse.up();
235
+ await sleep(200);
236
+ debugPage(`end mouse drag from ${from.x}, ${from.y} to ${to.x}, ${to.y}`);
237
+ }
238
+ };
239
+ }
240
+ get keyboard() {
241
+ return {
242
+ type: async (text)=>{
243
+ debugPage(`keyboard type ${text}`);
244
+ return this.underlyingPage.keyboard.type(text, {
245
+ delay: 80
246
+ });
247
+ },
248
+ press: async (action)=>{
249
+ const keys = Array.isArray(action) ? action : [
250
+ action
251
+ ];
252
+ debugPage('keyboard press', keys);
253
+ for (const k of keys){
254
+ const commands = k.command ? [
255
+ k.command
256
+ ] : [];
257
+ await this.underlyingPage.keyboard.down(k.key, {
258
+ commands
259
+ });
260
+ }
261
+ for (const k of [
262
+ ...keys
263
+ ].reverse())await this.underlyingPage.keyboard.up(k.key);
264
+ },
265
+ down: async (key)=>{
266
+ debugPage(`keyboard down ${key}`);
267
+ return this.underlyingPage.keyboard.down(key);
268
+ },
269
+ up: async (key)=>{
270
+ debugPage(`keyboard up ${key}`);
271
+ return this.underlyingPage.keyboard.up(key);
272
+ }
273
+ };
274
+ }
275
+ async clearInput(element) {
276
+ if (!element) return void console.warn('No element to clear input');
277
+ const backspace = async ()=>{
278
+ await sleep(100);
279
+ await this.keyboard.press([
280
+ {
281
+ key: 'Backspace'
282
+ }
283
+ ]);
284
+ };
285
+ const isMac = 'darwin' === process.platform;
286
+ debugPage('clearInput begin');
287
+ if (isMac) {
288
+ if ('puppeteer' === this.interfaceType) {
289
+ await this.mouse.click(element.center[0], element.center[1], {
290
+ count: 3
291
+ });
292
+ await backspace();
293
+ }
294
+ await this.mouse.click(element.center[0], element.center[1]);
295
+ await this.underlyingPage.keyboard.down('Meta');
296
+ await this.underlyingPage.keyboard.press('a');
297
+ await this.underlyingPage.keyboard.up('Meta');
298
+ await backspace();
299
+ } else {
300
+ await this.mouse.click(element.center[0], element.center[1]);
301
+ await this.underlyingPage.keyboard.down('Control');
302
+ await this.underlyingPage.keyboard.press('a');
303
+ await this.underlyingPage.keyboard.up('Control');
304
+ await backspace();
305
+ }
306
+ debugPage('clearInput end');
307
+ }
308
+ async moveToPointBeforeScroll(point) {
309
+ if (point) await this.mouse.move(point.left, point.top);
310
+ else if (!this.everMoved) {
311
+ const size = await this.size();
312
+ const targetX = Math.floor(size.width / 2);
313
+ const targetY = Math.floor(size.height / 2);
314
+ await this.mouse.move(targetX, targetY);
315
+ }
316
+ }
317
+ async scrollUntilTop(startingPoint) {
318
+ await this.moveToPointBeforeScroll(startingPoint);
319
+ return this.mouse.wheel(0, -9999999);
320
+ }
321
+ async scrollUntilBottom(startingPoint) {
322
+ await this.moveToPointBeforeScroll(startingPoint);
323
+ return this.mouse.wheel(0, 9999999);
324
+ }
325
+ async scrollUntilLeft(startingPoint) {
326
+ await this.moveToPointBeforeScroll(startingPoint);
327
+ return this.mouse.wheel(-9999999, 0);
328
+ }
329
+ async scrollUntilRight(startingPoint) {
330
+ await this.moveToPointBeforeScroll(startingPoint);
331
+ return this.mouse.wheel(9999999, 0);
332
+ }
333
+ async scrollUp(distance, startingPoint) {
334
+ const innerHeight = await this.evaluate(()=>window.innerHeight);
335
+ const scrollDistance = distance || 0.7 * innerHeight;
336
+ await this.moveToPointBeforeScroll(startingPoint);
337
+ return this.mouse.wheel(0, -scrollDistance);
338
+ }
339
+ async scrollDown(distance, startingPoint) {
340
+ const innerHeight = await this.evaluate(()=>window.innerHeight);
341
+ const scrollDistance = distance || 0.7 * innerHeight;
342
+ await this.moveToPointBeforeScroll(startingPoint);
343
+ return this.mouse.wheel(0, scrollDistance);
344
+ }
345
+ async scrollLeft(distance, startingPoint) {
346
+ const innerWidth = await this.evaluate(()=>window.innerWidth);
347
+ const scrollDistance = distance || 0.7 * innerWidth;
348
+ await this.moveToPointBeforeScroll(startingPoint);
349
+ return this.mouse.wheel(-scrollDistance, 0);
350
+ }
351
+ async scrollRight(distance, startingPoint) {
352
+ const innerWidth = await this.evaluate(()=>window.innerWidth);
353
+ const scrollDistance = distance || 0.7 * innerWidth;
354
+ await this.moveToPointBeforeScroll(startingPoint);
355
+ return this.mouse.wheel(scrollDistance, 0);
356
+ }
357
+ async navigate(url) {
358
+ debugPage(`navigate to ${url}`);
359
+ if ('puppeteer' === this.interfaceType) await this.underlyingPage.goto(url);
360
+ else if ('playwright' === this.interfaceType) await this.underlyingPage.goto(url);
361
+ else throw new Error('Unsupported page type for navigate');
362
+ }
363
+ async beforeInvokeAction(name, param) {
364
+ await Promise.all([
365
+ this.waitForNavigation(),
366
+ this.waitForNetworkIdle()
367
+ ]);
368
+ if (this.onBeforeInvokeAction) await this.onBeforeInvokeAction(name, param);
369
+ }
370
+ async afterInvokeAction(name, param) {
371
+ await Promise.all([
372
+ this.waitForNavigation(),
373
+ this.waitForNetworkIdle()
374
+ ]);
375
+ if (this.onAfterInvokeAction) await this.onAfterInvokeAction(name, param);
376
+ }
377
+ async destroy() {}
378
+ async getContext() {
379
+ return await WebPageContextParser(this, {});
380
+ }
381
+ async swipe(from, to, duration) {
382
+ const LONG_PRESS_THRESHOLD = 500;
383
+ const MIN_PRESS_THRESHOLD = 150;
384
+ duration = duration || 100;
385
+ if (duration < MIN_PRESS_THRESHOLD) duration = MIN_PRESS_THRESHOLD;
386
+ if (duration > LONG_PRESS_THRESHOLD) duration = LONG_PRESS_THRESHOLD;
387
+ debugPage(`mouse swipe from ${from.x}, ${from.y} to ${to.x}, ${to.y} with duration ${duration}ms`);
388
+ if ('puppeteer' === this.interfaceType) {
389
+ const page = this.underlyingPage;
390
+ await page.mouse.move(from.x, from.y);
391
+ await page.mouse.down({
392
+ button: 'left'
393
+ });
394
+ const steps = 30;
395
+ const delay = duration / steps;
396
+ for(let i = 1; i <= steps; i++){
397
+ const x = from.x + (to.x - from.x) * (i / steps);
398
+ const y = from.y + (to.y - from.y) * (i / steps);
399
+ await page.mouse.move(x, y);
400
+ await new Promise((resolve)=>setTimeout(resolve, delay));
401
+ }
402
+ await page.mouse.up({
403
+ button: 'left'
404
+ });
405
+ } else if ('playwright' === this.interfaceType) {
406
+ const page = this.underlyingPage;
407
+ await page.mouse.move(from.x, from.y);
408
+ await page.mouse.down();
409
+ const steps = 30;
410
+ const delay = duration / steps;
411
+ for(let i = 1; i <= steps; i++){
412
+ const x = from.x + (to.x - from.x) * (i / steps);
413
+ const y = from.y + (to.y - from.y) * (i / steps);
414
+ await page.mouse.move(x, y);
415
+ await page.waitForTimeout(delay);
416
+ }
417
+ await page.mouse.up({
418
+ button: 'left'
419
+ });
420
+ }
421
+ }
422
+ async longPress(x, y, duration) {
423
+ duration = duration || 500;
424
+ const LONG_PRESS_THRESHOLD = 600;
425
+ const MIN_PRESS_THRESHOLD = 300;
426
+ if (duration > LONG_PRESS_THRESHOLD) duration = LONG_PRESS_THRESHOLD;
427
+ if (duration < MIN_PRESS_THRESHOLD) duration = MIN_PRESS_THRESHOLD;
428
+ debugPage(`mouse longPress at ${x}, ${y} for ${duration}ms`);
429
+ if ('puppeteer' === this.interfaceType) {
430
+ const page = this.underlyingPage;
431
+ await page.mouse.move(x, y);
432
+ await page.mouse.down({
433
+ button: 'left'
434
+ });
435
+ await new Promise((res)=>setTimeout(res, duration));
436
+ await page.mouse.up({
437
+ button: 'left'
438
+ });
439
+ } else if ('playwright' === this.interfaceType) {
440
+ const page = this.underlyingPage;
441
+ await page.mouse.move(x, y);
442
+ await page.mouse.down({
443
+ button: 'left'
444
+ });
445
+ await page.waitForTimeout(duration);
446
+ await page.mouse.up({
447
+ button: 'left'
448
+ });
449
+ }
450
+ }
451
+ constructor(underlyingPage, interfaceType, opts){
452
+ _define_property(this, "underlyingPage", void 0);
453
+ _define_property(this, "waitForNavigationTimeout", void 0);
454
+ _define_property(this, "waitForNetworkIdleTimeout", void 0);
455
+ _define_property(this, "viewportSize", void 0);
456
+ _define_property(this, "onBeforeInvokeAction", void 0);
457
+ _define_property(this, "onAfterInvokeAction", void 0);
458
+ _define_property(this, "customActions", void 0);
459
+ _define_property(this, "interfaceType", void 0);
460
+ _define_property(this, "everMoved", false);
461
+ this.underlyingPage = underlyingPage;
462
+ this.interfaceType = interfaceType;
463
+ this.waitForNavigationTimeout = (null == opts ? void 0 : opts.waitForNavigationTimeout) ?? DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT;
464
+ this.waitForNetworkIdleTimeout = (null == opts ? void 0 : opts.waitForNetworkIdleTimeout) ?? DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT;
465
+ this.onBeforeInvokeAction = null == opts ? void 0 : opts.beforeInvokeAction;
466
+ this.onAfterInvokeAction = null == opts ? void 0 : opts.afterInvokeAction;
467
+ this.customActions = null == opts ? void 0 : opts.customActions;
468
+ }
469
+ }
470
+ function forceClosePopup(page, debugProfile) {
471
+ page.on('popup', async (popup)=>{
472
+ if (!popup) return void console.warn('got a popup event, but the popup is not ready yet, skip');
473
+ const url = await popup.url();
474
+ console.log(`Popup opened: ${url}`);
475
+ if (popup.isClosed()) debugProfile(`popup is already closed, skip close ${url}`);
476
+ else try {
477
+ await popup.close();
478
+ } catch (error) {
479
+ debugProfile(`failed to close popup ${url}, error: ${error}`);
480
+ }
481
+ if (page.isClosed()) debugProfile(`page is already closed, skip goto ${url}`);
482
+ else try {
483
+ await page.goto(url);
484
+ } catch (error) {
485
+ debugProfile(`failed to goto ${url}, error: ${error}`);
486
+ }
487
+ });
488
+ }
489
+ export { Page, debugPage, forceClosePopup };
490
+
491
+ //# sourceMappingURL=base-page.mjs.map