misoai-web 1.5.6 → 1.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/agent.js +124 -21
- package/dist/es/agent.js.map +1 -1
- package/dist/es/bridge-mode-browser.js +10 -9
- package/dist/es/bridge-mode-browser.js.map +1 -1
- package/dist/es/bridge-mode.js +126 -23
- package/dist/es/bridge-mode.js.map +1 -1
- package/dist/es/chrome-extension.js +132 -28
- package/dist/es/chrome-extension.js.map +1 -1
- package/dist/es/index.js +140 -21
- package/dist/es/index.js.map +1 -1
- package/dist/es/midscene-playground.js +124 -21
- package/dist/es/midscene-playground.js.map +1 -1
- package/dist/es/midscene-server.js.map +1 -1
- package/dist/es/playground.js +124 -21
- package/dist/es/playground.js.map +1 -1
- package/dist/es/playwright-report.js +1 -1
- package/dist/es/playwright-report.js.map +1 -1
- package/dist/es/playwright.js +140 -21
- package/dist/es/playwright.js.map +1 -1
- package/dist/es/puppeteer-agent-launcher.js +124 -21
- package/dist/es/puppeteer-agent-launcher.js.map +1 -1
- package/dist/es/puppeteer.js +124 -21
- package/dist/es/puppeteer.js.map +1 -1
- package/dist/es/ui-utils.js.map +1 -1
- package/dist/es/utils.js +7 -4
- package/dist/es/utils.js.map +1 -1
- package/dist/es/yaml.js +24 -0
- package/dist/es/yaml.js.map +1 -1
- package/dist/lib/agent.js +122 -19
- package/dist/lib/agent.js.map +1 -1
- package/dist/lib/bridge-mode-browser.js +10 -9
- package/dist/lib/bridge-mode-browser.js.map +1 -1
- package/dist/lib/bridge-mode.js +124 -21
- package/dist/lib/bridge-mode.js.map +1 -1
- package/dist/lib/chrome-extension.js +130 -26
- package/dist/lib/chrome-extension.js.map +1 -1
- package/dist/lib/index.js +138 -19
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/midscene-playground.js +122 -19
- package/dist/lib/midscene-playground.js.map +1 -1
- package/dist/lib/midscene-server.js.map +1 -1
- package/dist/lib/playground.js +122 -19
- package/dist/lib/playground.js.map +1 -1
- package/dist/lib/playwright-report.js +1 -1
- package/dist/lib/playwright-report.js.map +1 -1
- package/dist/lib/playwright.js +138 -19
- package/dist/lib/playwright.js.map +1 -1
- package/dist/lib/puppeteer-agent-launcher.js +122 -19
- package/dist/lib/puppeteer-agent-launcher.js.map +1 -1
- package/dist/lib/puppeteer.js +122 -19
- package/dist/lib/puppeteer.js.map +1 -1
- package/dist/lib/ui-utils.js.map +1 -1
- package/dist/lib/utils.js +7 -4
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/yaml.js +24 -0
- package/dist/lib/yaml.js.map +1 -1
- package/dist/types/agent.d.ts +10 -7
- package/dist/types/bridge-mode-browser.d.ts +2 -3
- package/dist/types/bridge-mode.d.ts +2 -3
- package/dist/types/{browser-aec1055d.d.ts → browser-9b472ffb.d.ts} +1 -1
- package/dist/types/chrome-extension.d.ts +2 -3
- package/dist/types/index.d.ts +1 -2
- package/dist/types/midscene-server.d.ts +1 -2
- package/dist/types/{page-86ab0fe1.d.ts → page-ed0ecb44.d.ts} +19 -9
- package/dist/types/playground.d.ts +2 -3
- package/dist/types/playwright.d.ts +9 -2
- package/dist/types/puppeteer-agent-launcher.d.ts +1 -2
- package/dist/types/puppeteer.d.ts +6 -5
- package/dist/types/ui-utils.d.ts +1 -1
- package/dist/types/utils.d.ts +1 -2
- package/dist/types/yaml.d.ts +1 -2
- package/iife-script/htmlElement.js +51 -73
- package/iife-script/htmlElementDebug.js +33 -54
- package/package.json +23 -23
- package/LICENSE +0 -21
@@ -52,7 +52,7 @@ function printReportMsg(filepath) {
|
|
52
52
|
(0, import_utils2.logMsg)(`Midscene - report file updated: ${filepath}`);
|
53
53
|
}
|
54
54
|
function replaceIllegalPathCharsAndSpace(str) {
|
55
|
-
return str.replace(/[
|
55
|
+
return str.replace(/[:*?"<>| ]/g, "-");
|
56
56
|
}
|
57
57
|
|
58
58
|
// src/playwright/reporter/index.ts
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACQA,sBAAiD;AACjD,mBAAuC;AACvC,iBAAsD;AAEtD,uBAKO;AACP,iBAAgC;AAEhC,IAAAA,gBAAqC;AACrC,mBAAkB;AAoEX,SAAS,eAAe,MAAM,OAAO;AAC1C,QAAM,oBAAgB,wBAAY,mCAAwB;AAC1D,QAAM,yBAAqB,aAAAC,SAAM,EAAE,OAAO,qBAAqB;AAE/D,QAAM,eAAW,oBAAK,EAAE,UAAU,GAAG,CAAC;AACtC,SAAO,GAAG,iBAAiB,GAAG,IAAI,kBAAkB,IAAI,QAAQ;AAClE;AAEO,SAAS,eAAe,UAAkB;AAC/C,4BAAO,mCAAmC,QAAQ,EAAE;AACtD;AA0DO,SAAS,gCAAgC,KAAa;AAC3D,SAAO,IAAI,QAAQ,kBAAkB,GAAG;AAC1C;;;ADzJA,IAAAD,gBAAgC;AAUhC,SAAS,UAAU,SAAgB;AACjC,MAAI,QAAQ,IAAI,UAAU,QAAQ;AAChC,YAAQ,IAAI,wBAAwB,GAAG,OAAO;AAAA,EAChD;AACF;AAEA,IAAM,eAAgD,CAAC;AACvD,IAAI;AACJ,IAAM,sBAA2C,oBAAI,IAAI;AAEzD,SAAS,kBAAkB,WAA2B;AACpD,MAAI,CAAC,oBAAoB,IAAI,SAAS,GAAG;AAGvC,UAAM,UAAU,cAAc,gCAAgC,SAAS,CAAC;AACxE,UAAM,oBAAoB,eAAe,OAAO;AAChD,wBAAoB,IAAI,WAAW,iBAAiB;AAAA,EACtD;AACA,SAAO,oBAAoB,IAAI,SAAS;AAC1C;AAEA,SAAS,aAAa,MAA6B,QAAiB;AAClE,MAAI,SAAS,YAAY;AAEvB,UAAM,WAAW,aAAa;AAAA,MAC5B,CAAC,SAAS,KAAK,YAAY,uBAAuB;AAAA,IACpD;AAEA,QAAI,UAAU;AAEZ,YAAM,iBAAiB;AAAA,QACrB,SAAS,YAAY;AAAA,MACvB;AAEA,YAAM,iBAAa,+BAAgB,gBAAgB,CAAC,QAAQ,CAAC;AAC7D,oBAAc,eAAe,UAAU;AAAA,IACzC;AAAA,EACF,WAAW,SAAS,UAAU;AAE5B,QAAI,CAAC,gBAAgB;AACnB,uBAAiB,eAAe,mBAAmB;AAAA,IACrD;AAEA,UAAM,iBAAa,+BAAgB,gBAAgB,YAAY;AAC/D,kBAAc,eAAe,UAAU;AAAA,EACzC,OAAO;AACL,UAAM,IAAI;AAAA,MACR,+CAA+C,IAAI;AAAA,IACrD;AAAA,EACF;AACF;AAEA,SAAS,QAAQ,cAAsB;AACrC,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AAEA,MAAI,iBAAiB,YAAY,iBAAiB,YAAY;AAC5D,UAAM,IAAI;AAAA,MACR,+CAA+C,YAAY;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO;AACT;AAEA,IAAM,mBAAN,MAA2C;AAAA,EAGzC,MAAM,QAAQ,QAAoB,OAAc;AAC9C,UAAM,eAAe,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG;AAEhD,SAAK,OAAO,QAAQ,YAAY;AAAA,EAIlC;AAAA,EAEA,YAAY,MAAgB,SAAqB;AAAA,EAEjD;AAAA,EAEA,UAAU,MAAgB,QAAoB;AAC5C,UAAM,iBAAiB,KAAK,YAAY,KAAK,CAAC,eAAe;AAC3D,aAAO,WAAW,SAAS;AAAA,IAC7B,CAAC;AACD,QAAI,CAAC,gBAAgB;AAAa;AAClC,UAAM,QAAQ,OAAO,QAAQ,WAAW,OAAO,KAAK,MAAM;AAC1D,UAAM,SAAS,GAAG,KAAK,EAAE,GAAG,KAAK;AACjC,UAAM,WAAqC;AAAA,MACzC,YAAY,eAAe;AAAA,MAC3B,YAAY;AAAA,QACV,oBAAoB;AAAA,QACpB,uBAAuB,GAAG,KAAK,KAAK,GAAG,KAAK;AAAA,QAC5C,wBAAwB,OAAO;AAAA,QAC/B,0BAA0B,OAAO;AAAA,MACnC;AAAA,IACF;AAEA,iBAAa,KAAK,QAAQ;AAE1B,iBAAa,KAAK,MAAO,MAAM;AAE/B,SAAK,cAAc,KAAK,YAAY;AAAA,MAClC,CAAC,eAAe,WAAW,SAAS;AAAA,IACtC;AAAA,EACF;AAAA,EAEA,MAAM,QAAoB;AACxB,iBAAa,KAAK,IAAK;AAEvB,WAAO,qBAAqB,OAAO,MAAM,EAAE;AAAA,EAC7C;AACF;AAEA,IAAO,mBAAQ","names":["import_utils","dayjs"],"ignoreList":[],"sources":["../../src/playwright/reporter/index.ts","../../src/common/utils.ts"],"sourcesContent":["import {\n printReportMsg,\n replaceIllegalPathCharsAndSpace,\n reportFileName,\n} from '@/common/utils';\nimport type { ReportDumpWithAttributes } from 'misoai-core';\nimport { writeDumpReport } from 'misoai-core/utils';\nimport type {\n FullConfig,\n FullResult,\n Reporter,\n Suite,\n TestCase,\n TestResult,\n} from '@playwright/test/reporter';\n\nfunction logger(...message: any[]) {\n if (process.env.DEBUG === 'true') {\n console.log('Midscene e2e report:', ...message);\n }\n}\n\nconst testDataList: Array<ReportDumpWithAttributes> = [];\nlet mergedFilename: string;\nconst testTitleToFilename: Map<string, string> = new Map();\n\nfunction getStableFilename(testTitle: string): string {\n if (!testTitleToFilename.has(testTitle)) {\n // use reportFileName to generate the base filename\n // only replace the illegal characters in the file system: /, \\, :, *, ?, \", <, >, |\n const baseTag = `playwright-${replaceIllegalPathCharsAndSpace(testTitle)}`;\n const generatedFilename = reportFileName(baseTag);\n testTitleToFilename.set(testTitle, generatedFilename);\n }\n return testTitleToFilename.get(testTitle)!;\n}\n\nfunction updateReport(mode: 'merged' | 'separate', testId?: string) {\n if (mode === 'separate') {\n // in separate mode, find the data for the corresponding testID and generate a separate report\n const testData = testDataList.find(\n (data) => data.attributes?.playwright_test_id === testId,\n );\n\n if (testData) {\n // use the stable filename\n const stableFilename = getStableFilename(\n testData.attributes?.playwright_test_title,\n );\n\n const reportPath = writeDumpReport(stableFilename, [testData]);\n reportPath && printReportMsg(reportPath);\n }\n } else if (mode === 'merged') {\n // in merged mode, write all test data into one file\n if (!mergedFilename) {\n mergedFilename = reportFileName('playwright-merged');\n }\n\n const reportPath = writeDumpReport(mergedFilename, testDataList);\n reportPath && printReportMsg(reportPath);\n } else {\n throw new Error(\n `Unknown reporter type in playwright config: ${mode}, only support 'merged' or 'separate'`,\n );\n }\n}\n\nfunction getMode(reporterType: string) {\n if (!reporterType) {\n return 'merged';\n }\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\n return reporterType;\n}\n\nclass MidsceneReporter implements Reporter {\n mode?: 'merged' | 'separate';\n\n async onBegin(config: FullConfig, suite: Suite) {\n const reporterType = config.reporter?.[1]?.[1]?.type;\n\n this.mode = getMode(reporterType);\n\n // const suites = suite.allTests();\n // logger(`Starting the run with ${suites.length} tests`);\n }\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 === 'MIDSCENE_DUMP_ANNOTATION';\n });\n if (!dumpAnnotation?.description) return;\n const retry = result.retry ? `(retry #${result.retry})` : '';\n const testId = `${test.id}${retry}`;\n const testData: ReportDumpWithAttributes = {\n dumpString: dumpAnnotation.description,\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 testDataList.push(testData);\n\n updateReport(this.mode!, testId);\n\n test.annotations = test.annotations.filter(\n (annotation) => annotation.type !== 'MIDSCENE_DUMP_ANNOTATION',\n );\n }\n\n onEnd(result: FullResult) {\n updateReport(this.mode!);\n\n logger(`Finished the run: ${result.status}`);\n }\n}\n\nexport default MidsceneReporter;\n","import type { StaticPage } from '@/playground';\nimport type {\n BaseElement,\n ElementTreeNode,\n PlanningLocateParam,\n PlaywrightParserOpt,\n UIContext,\n} from 'misoai-core';\nimport { elementByPositionWithElementInfo } from 'misoai-core/ai-model';\nimport { uploadTestInfoToServer } from 'misoai-core/utils';\nimport { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from 'misoai-shared/env';\nimport type { ElementInfo } from 'misoai-shared/extractor';\nimport {\n generateElementByPosition,\n getNodeFromCacheList,\n traverseTree,\n treeToList,\n} from 'misoai-shared/extractor';\nimport { resizeImgBase64 } from 'misoai-shared/img';\nimport type { DebugFunction } from 'misoai-shared/logger';\nimport { assert, logMsg, uuid } from 'misoai-shared/utils';\nimport dayjs from 'dayjs';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport type { Page as PuppeteerPage } from 'puppeteer';\nimport { WebElementInfo } from '../web-element';\nimport type { WebPage } from './page';\n\nexport type WebUIContext = UIContext<WebElementInfo> & {\n url: string;\n};\n\nexport async function parseContextFromWebPage(\n page: WebPage,\n _opt?: PlaywrightParserOpt,\n): Promise<WebUIContext> {\n assert(page, 'page is required');\n if ((page as StaticPage)._forceUsePageContext) {\n return await (page as any)._forceUsePageContext();\n }\n const url = await page.url();\n uploadTestInfoToServer({ testUrl: url });\n\n let screenshotBase64: string;\n let tree: ElementTreeNode<ElementInfo>;\n\n await Promise.all([\n page.screenshotBase64().then((base64) => {\n screenshotBase64 = base64;\n }),\n page.getElementsNodeTree().then(async (treeRoot) => {\n tree = treeRoot;\n }),\n ]);\n\n const webTree = traverseTree(tree!, (elementInfo) => {\n const { rect, id, content, attributes, locator, indexId } = elementInfo;\n return new WebElementInfo({\n rect,\n locator,\n id,\n content,\n attributes,\n indexId,\n });\n });\n\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n const elementsInfo = treeToList(webTree);\n const size = await page.size();\n\n if (size.dpr && size.dpr > 1) {\n // console.time('resizeImgBase64');\n screenshotBase64 = await resizeImgBase64(screenshotBase64, {\n width: size.width,\n height: size.height,\n });\n // console.timeEnd('resizeImgBase64');\n }\n\n return {\n content: elementsInfo!,\n tree: webTree,\n size,\n screenshotBase64: screenshotBase64!,\n url,\n };\n}\n\nexport function reportFileName(tag = 'web') {\n const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\n // ensure uniqueness at the same time\n const uniqueId = uuid().substring(0, 8);\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\n}\n\nexport function printReportMsg(filepath: string) {\n logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\n/**\n * Get the current execution file name\n * @returns The name of the current execution file\n */\nexport function getCurrentExecutionFile(trace?: string): string | false {\n const error = new Error();\n const stackTrace = trace || error.stack;\n const pkgDir = process.cwd() || '';\n if (stackTrace) {\n const stackLines = stackTrace.split('\\n');\n for (const line of stackLines) {\n if (\n line.includes('.spec.') ||\n line.includes('.test.') ||\n line.includes('.ts') ||\n line.includes('.js')\n ) {\n const match = line.match(/(?:at\\s+)?(.*?\\.(?:spec|test)\\.[jt]s)/);\n if (match?.[1]) {\n const targetFileName = match[1]\n .replace(pkgDir, '')\n .trim()\n .replace('at ', '');\n return targetFileName;\n }\n }\n }\n }\n return false;\n}\n\nconst testFileIndex = new Map<string, number>();\n\nexport function generateCacheId(fileName?: string): string {\n let taskFile = fileName || getCurrentExecutionFile();\n if (!taskFile) {\n taskFile = uuid();\n console.warn(\n 'Midscene - using random UUID for cache id. Cache may be invalid.',\n );\n }\n\n if (testFileIndex.has(taskFile)) {\n const currentIndex = testFileIndex.get(taskFile);\n if (currentIndex !== undefined) {\n testFileIndex.set(taskFile, currentIndex + 1);\n }\n } else {\n testFileIndex.set(taskFile, 1);\n }\n return `${taskFile}-${testFileIndex.get(taskFile)}`;\n}\n\nexport const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =\n 'NOT_IMPLEMENTED_AS_DESIGNED';\n\nexport function replaceIllegalPathCharsAndSpace(str: string) {\n return str.replace(/[/\\\\:*?\"<>| ]/g, '-');\n}\n\nexport function forceClosePopup(\n page: PuppeteerPage | PlaywrightPage,\n debug: DebugFunction,\n) {\n page.on('popup', async (popup) => {\n if (!popup) {\n console.warn('got a popup event, but the popup is not ready yet, skip');\n return;\n }\n const url = await (popup as PuppeteerPage).url();\n console.log(`Popup opened: ${url}`);\n if (!(popup as PuppeteerPage).isClosed()) {\n try {\n await (popup as PuppeteerPage).close(); // Close the newly opened TAB\n } catch (error) {\n debug(`failed to close popup ${url}, error: ${error}`);\n }\n } else {\n debug(`popup is already closed, skip close ${url}`);\n }\n\n if (!page.isClosed()) {\n try {\n await page.goto(url);\n } catch (error) {\n debug(`failed to goto ${url}, error: ${error}`);\n }\n } else {\n debug(`page is already closed, skip goto ${url}`);\n }\n });\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParam,\n tree: ElementTreeNode<BaseElement>,\n) {\n if (!planLocateParam) {\n return undefined;\n }\n if (planLocateParam.id) {\n return getNodeFromCacheList(planLocateParam.id);\n }\n\n if (planLocateParam.bbox) {\n const centerPosition = {\n x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),\n y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2),\n };\n let element = elementByPositionWithElementInfo(tree, centerPosition);\n\n if (!element) {\n element = generateElementByPosition(centerPosition) as BaseElement;\n }\n\n return element;\n }\n\n return undefined;\n}\n"]}
|
1
|
+
{"version":3,"mappings":"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","names":["import_utils","dayjs"],"ignoreList":[],"sources":["../../src/playwright/reporter/index.ts","../../src/common/utils.ts"],"sourcesContent":["import {\n printReportMsg,\n replaceIllegalPathCharsAndSpace,\n reportFileName,\n} from '@/common/utils';\nimport type { ReportDumpWithAttributes } from 'misoai-core';\nimport { writeDumpReport } from 'misoai-core/utils';\nimport type {\n FullConfig,\n FullResult,\n Reporter,\n Suite,\n TestCase,\n TestResult,\n} from '@playwright/test/reporter';\n\nfunction logger(...message: any[]) {\n if (process.env.DEBUG === 'true') {\n console.log('Midscene e2e report:', ...message);\n }\n}\n\nconst testDataList: Array<ReportDumpWithAttributes> = [];\nlet mergedFilename: string;\nconst testTitleToFilename: Map<string, string> = new Map();\n\nfunction getStableFilename(testTitle: string): string {\n if (!testTitleToFilename.has(testTitle)) {\n // use reportFileName to generate the base filename\n // only replace the illegal characters in the file system: /, \\, :, *, ?, \", <, >, |\n const baseTag = `playwright-${replaceIllegalPathCharsAndSpace(testTitle)}`;\n const generatedFilename = reportFileName(baseTag);\n testTitleToFilename.set(testTitle, generatedFilename);\n }\n return testTitleToFilename.get(testTitle)!;\n}\n\nfunction updateReport(mode: 'merged' | 'separate', testId?: string) {\n if (mode === 'separate') {\n // in separate mode, find the data for the corresponding testID and generate a separate report\n const testData = testDataList.find(\n (data) => data.attributes?.playwright_test_id === testId,\n );\n\n if (testData) {\n // use the stable filename\n const stableFilename = getStableFilename(\n testData.attributes?.playwright_test_title,\n );\n\n const reportPath = writeDumpReport(stableFilename, [testData]);\n reportPath && printReportMsg(reportPath);\n }\n } else if (mode === 'merged') {\n // in merged mode, write all test data into one file\n if (!mergedFilename) {\n mergedFilename = reportFileName('playwright-merged');\n }\n\n const reportPath = writeDumpReport(mergedFilename, testDataList);\n reportPath && printReportMsg(reportPath);\n } else {\n throw new Error(\n `Unknown reporter type in playwright config: ${mode}, only support 'merged' or 'separate'`,\n );\n }\n}\n\nfunction getMode(reporterType: string) {\n if (!reporterType) {\n return 'merged';\n }\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\n return reporterType;\n}\n\nclass MidsceneReporter implements Reporter {\n mode?: 'merged' | 'separate';\n\n async onBegin(config: FullConfig, suite: Suite) {\n const reporterType = config.reporter?.[1]?.[1]?.type;\n\n this.mode = getMode(reporterType);\n\n // const suites = suite.allTests();\n // logger(`Starting the run with ${suites.length} tests`);\n }\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 === 'MIDSCENE_DUMP_ANNOTATION';\n });\n if (!dumpAnnotation?.description) return;\n const retry = result.retry ? `(retry #${result.retry})` : '';\n const testId = `${test.id}${retry}`;\n const testData: ReportDumpWithAttributes = {\n dumpString: dumpAnnotation.description,\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 testDataList.push(testData);\n\n updateReport(this.mode!, testId);\n\n test.annotations = test.annotations.filter(\n (annotation) => annotation.type !== 'MIDSCENE_DUMP_ANNOTATION',\n );\n }\n\n onEnd(result: FullResult) {\n updateReport(this.mode!);\n\n logger(`Finished the run: ${result.status}`);\n }\n}\n\nexport default MidsceneReporter;\n","import type { StaticPage } from '@/playground';\nimport type {\n BaseElement,\n ElementTreeNode,\n PlanningLocateParam,\n PlaywrightParserOpt,\n UIContext,\n} from 'misoai-core';\nimport { elementByPositionWithElementInfo } from 'misoai-core/ai-model';\nimport { uploadTestInfoToServer } from 'misoai-core/utils';\nimport { MIDSCENE_REPORT_TAG_NAME, getAIConfig } from 'misoai-shared/env';\nimport type { ElementInfo } from 'misoai-shared/extractor';\nimport {\n generateElementByPosition,\n getNodeFromCacheList,\n traverseTree,\n treeToList,\n} from 'misoai-shared/extractor';\nimport { resizeImgBase64 } from 'misoai-shared/img';\nimport type { DebugFunction } from 'misoai-shared/logger';\nimport { assert, logMsg, uuid } from 'misoai-shared/utils';\nimport dayjs from 'dayjs';\nimport type { Page as PlaywrightPage } from 'playwright';\nimport type { Page as PuppeteerPage } from 'puppeteer';\nimport { WebElementInfo } from '../web-element';\nimport type { WebPage } from './page';\n\nexport type WebUIContext = UIContext<WebElementInfo> & {\n url: string;\n};\n\nexport async function parseContextFromWebPage(\n page: WebPage,\n _opt?: PlaywrightParserOpt,\n): Promise<WebUIContext> {\n assert(page, 'page is required');\n if ((page as StaticPage)._forceUsePageContext) {\n return await (page as any)._forceUsePageContext();\n }\n const url = await page.url();\n uploadTestInfoToServer({ testUrl: url });\n\n let screenshotBase64: string;\n let tree: ElementTreeNode<ElementInfo>;\n\n await Promise.all([\n page.screenshotBase64().then((base64) => {\n screenshotBase64 = base64;\n }),\n page.getElementsNodeTree().then(async (treeRoot) => {\n tree = treeRoot;\n }),\n ]);\n\n const webTree = traverseTree(tree!, (elementInfo) => {\n const { rect, id, content, attributes, locator, indexId, isVisible } =\n elementInfo;\n return new WebElementInfo({\n rect,\n locator,\n id,\n content,\n attributes,\n indexId,\n isVisible,\n });\n });\n\n assert(screenshotBase64!, 'screenshotBase64 is required');\n\n const elementsInfo = treeToList(webTree);\n const size = await page.size();\n\n if (size.dpr && size.dpr > 1) {\n // console.time('resizeImgBase64');\n screenshotBase64 = await resizeImgBase64(screenshotBase64, {\n width: size.width,\n height: size.height,\n });\n // console.timeEnd('resizeImgBase64');\n }\n\n return {\n content: elementsInfo!,\n tree: webTree,\n size,\n screenshotBase64: screenshotBase64!,\n url,\n };\n}\n\nexport function reportFileName(tag = 'web') {\n const reportTagName = getAIConfig(MIDSCENE_REPORT_TAG_NAME);\n const dateTimeInFileName = dayjs().format('YYYY-MM-DD_HH-mm-ss');\n // ensure uniqueness at the same time\n const uniqueId = uuid().substring(0, 8);\n return `${reportTagName || tag}-${dateTimeInFileName}-${uniqueId}`;\n}\n\nexport function printReportMsg(filepath: string) {\n logMsg(`Midscene - report file updated: ${filepath}`);\n}\n\n/**\n * Get the current execution file name\n * @returns The name of the current execution file\n */\nexport function getCurrentExecutionFile(trace?: string): string | false {\n const error = new Error();\n const stackTrace = trace || error.stack;\n const pkgDir = process.cwd() || '';\n if (stackTrace) {\n const stackLines = stackTrace.split('\\n');\n for (const line of stackLines) {\n if (\n line.includes('.spec.') ||\n line.includes('.test.') ||\n line.includes('.ts') ||\n line.includes('.js')\n ) {\n const match = line.match(/(?:at\\s+)?(.*?\\.(?:spec|test)\\.[jt]s)/);\n if (match?.[1]) {\n const targetFileName = match[1]\n .replace(pkgDir, '')\n .trim()\n .replace('at ', '');\n return targetFileName;\n }\n }\n }\n }\n return false;\n}\n\nconst testFileIndex = new Map<string, number>();\n\nexport function generateCacheId(fileName?: string): string {\n let taskFile = fileName || getCurrentExecutionFile();\n if (!taskFile) {\n taskFile = uuid();\n console.warn(\n 'Midscene - using random UUID for cache id. Cache may be invalid.',\n );\n }\n\n if (testFileIndex.has(taskFile)) {\n const currentIndex = testFileIndex.get(taskFile);\n if (currentIndex !== undefined) {\n testFileIndex.set(taskFile, currentIndex + 1);\n }\n } else {\n testFileIndex.set(taskFile, 1);\n }\n return `${taskFile}-${testFileIndex.get(taskFile)}`;\n}\n\nexport const ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED =\n 'NOT_IMPLEMENTED_AS_DESIGNED';\n\nexport function replaceIllegalPathCharsAndSpace(str: string) {\n // Only replace characters that are illegal in filenames, but preserve path separators\n return str.replace(/[:*?\"<>| ]/g, '-');\n}\n\nexport function forceClosePopup(\n page: PuppeteerPage | PlaywrightPage,\n debug: DebugFunction,\n) {\n page.on('popup', async (popup) => {\n if (!popup) {\n console.warn('got a popup event, but the popup is not ready yet, skip');\n return;\n }\n const url = await (popup as PuppeteerPage).url();\n console.log(`Popup opened: ${url}`);\n if (!(popup as PuppeteerPage).isClosed()) {\n try {\n await (popup as PuppeteerPage).close(); // Close the newly opened TAB\n } catch (error) {\n debug(`failed to close popup ${url}, error: ${error}`);\n }\n } else {\n debug(`popup is already closed, skip close ${url}`);\n }\n\n if (!page.isClosed()) {\n try {\n await page.goto(url);\n } catch (error) {\n debug(`failed to goto ${url}, error: ${error}`);\n }\n } else {\n debug(`page is already closed, skip goto ${url}`);\n }\n });\n}\n\nexport function matchElementFromPlan(\n planLocateParam: PlanningLocateParam,\n tree: ElementTreeNode<BaseElement>,\n) {\n if (!planLocateParam) {\n return undefined;\n }\n if (planLocateParam.id) {\n return getNodeFromCacheList(planLocateParam.id);\n }\n\n if (planLocateParam.bbox) {\n const centerPosition = {\n x: Math.floor((planLocateParam.bbox[0] + planLocateParam.bbox[2]) / 2),\n y: Math.floor((planLocateParam.bbox[1] + planLocateParam.bbox[3]) / 2),\n };\n let element = elementByPositionWithElementInfo(tree, centerPosition);\n\n if (!element) {\n element = generateElementByPosition(centerPosition) as BaseElement;\n }\n\n return element;\n }\n\n return undefined;\n}\n"]}
|
package/dist/lib/playwright.js
CHANGED
@@ -144,6 +144,10 @@ var ScriptPlayer = class {
|
|
144
144
|
} else if ("aiQuery" in flowItem) {
|
145
145
|
const queryTask = flowItem;
|
146
146
|
const prompt = queryTask.aiQuery;
|
147
|
+
const options = {
|
148
|
+
domIncluded: queryTask.domIncluded,
|
149
|
+
screenshotIncluded: queryTask.screenshotIncluded
|
150
|
+
};
|
147
151
|
(0, import_utils.assert)(prompt, "missing prompt for aiQuery");
|
148
152
|
(0, import_utils.assert)(
|
149
153
|
typeof prompt === "string",
|
@@ -154,6 +158,10 @@ var ScriptPlayer = class {
|
|
154
158
|
} else if ("aiNumber" in flowItem) {
|
155
159
|
const numberTask = flowItem;
|
156
160
|
const prompt = numberTask.aiNumber;
|
161
|
+
const options = {
|
162
|
+
domIncluded: numberTask.domIncluded,
|
163
|
+
screenshotIncluded: numberTask.screenshotIncluded
|
164
|
+
};
|
157
165
|
(0, import_utils.assert)(prompt, "missing prompt for number");
|
158
166
|
(0, import_utils.assert)(
|
159
167
|
typeof prompt === "string",
|
@@ -164,6 +172,10 @@ var ScriptPlayer = class {
|
|
164
172
|
} else if ("aiString" in flowItem) {
|
165
173
|
const stringTask = flowItem;
|
166
174
|
const prompt = stringTask.aiString;
|
175
|
+
const options = {
|
176
|
+
domIncluded: stringTask.domIncluded,
|
177
|
+
screenshotIncluded: stringTask.screenshotIncluded
|
178
|
+
};
|
167
179
|
(0, import_utils.assert)(prompt, "missing prompt for string");
|
168
180
|
(0, import_utils.assert)(
|
169
181
|
typeof prompt === "string",
|
@@ -174,6 +186,10 @@ var ScriptPlayer = class {
|
|
174
186
|
} else if ("aiBoolean" in flowItem) {
|
175
187
|
const booleanTask = flowItem;
|
176
188
|
const prompt = booleanTask.aiBoolean;
|
189
|
+
const options = {
|
190
|
+
domIncluded: booleanTask.domIncluded,
|
191
|
+
screenshotIncluded: booleanTask.screenshotIncluded
|
192
|
+
};
|
177
193
|
(0, import_utils.assert)(prompt, "missing prompt for boolean");
|
178
194
|
(0, import_utils.assert)(
|
179
195
|
typeof prompt === "string",
|
@@ -216,6 +232,9 @@ var ScriptPlayer = class {
|
|
216
232
|
} else if ("aiTap" in flowItem) {
|
217
233
|
const tapTask = flowItem;
|
218
234
|
await agent.aiTap(tapTask.aiTap, tapTask);
|
235
|
+
} else if ("aiRightClick" in flowItem) {
|
236
|
+
const rightClickTask = flowItem;
|
237
|
+
await agent.aiRightClick(rightClickTask.aiRightClick, rightClickTask);
|
219
238
|
} else if ("aiHover" in flowItem) {
|
220
239
|
const hoverTask = flowItem;
|
221
240
|
await agent.aiHover(hoverTask.aiHover, hoverTask);
|
@@ -238,6 +257,11 @@ var ScriptPlayer = class {
|
|
238
257
|
evaluateJavaScriptTask.javascript
|
239
258
|
);
|
240
259
|
this.setResult(evaluateJavaScriptTask.name, result);
|
260
|
+
} else if ("logScreenshot" in flowItem) {
|
261
|
+
const logScreenshotTask = flowItem;
|
262
|
+
await agent.logScreenshot(logScreenshotTask.logScreenshot, {
|
263
|
+
content: logScreenshotTask.content || ""
|
264
|
+
});
|
241
265
|
} else {
|
242
266
|
throw new Error(`unknown flowItem: ${JSON.stringify(flowItem)}`);
|
243
267
|
}
|
@@ -486,7 +510,8 @@ var WebElementInfo = class {
|
|
486
510
|
id,
|
487
511
|
attributes,
|
488
512
|
indexId,
|
489
|
-
xpaths
|
513
|
+
xpaths,
|
514
|
+
isVisible
|
490
515
|
}) {
|
491
516
|
this.content = content;
|
492
517
|
this.rect = rect;
|
@@ -499,6 +524,7 @@ var WebElementInfo = class {
|
|
499
524
|
this.attributes = attributes;
|
500
525
|
this.indexId = indexId;
|
501
526
|
this.xpaths = xpaths;
|
527
|
+
this.isVisible = isVisible;
|
502
528
|
}
|
503
529
|
};
|
504
530
|
|
@@ -521,14 +547,15 @@ async function parseContextFromWebPage(page, _opt) {
|
|
521
547
|
})
|
522
548
|
]);
|
523
549
|
const webTree = (0, import_extractor.traverseTree)(tree, (elementInfo) => {
|
524
|
-
const { rect, id, content, attributes, locator, indexId } = elementInfo;
|
550
|
+
const { rect, id, content, attributes, locator, indexId, isVisible } = elementInfo;
|
525
551
|
return new WebElementInfo({
|
526
552
|
rect,
|
527
553
|
locator,
|
528
554
|
id,
|
529
555
|
content,
|
530
556
|
attributes,
|
531
|
-
indexId
|
557
|
+
indexId,
|
558
|
+
isVisible
|
532
559
|
});
|
533
560
|
});
|
534
561
|
(0, import_utils4.assert)(screenshotBase64, "screenshotBase64 is required");
|
@@ -558,7 +585,7 @@ function printReportMsg(filepath) {
|
|
558
585
|
(0, import_utils4.logMsg)(`Midscene - report file updated: ${filepath}`);
|
559
586
|
}
|
560
587
|
function replaceIllegalPathCharsAndSpace(str) {
|
561
|
-
return str.replace(/[
|
588
|
+
return str.replace(/[:*?"<>| ]/g, "-");
|
562
589
|
}
|
563
590
|
function forceClosePopup(page, debug6) {
|
564
591
|
page.on("popup", async (popup) => {
|
@@ -872,10 +899,10 @@ var PageTaskExecutor = class {
|
|
872
899
|
if (!taskParam || !taskParam.value) {
|
873
900
|
return;
|
874
901
|
}
|
875
|
-
await this.page.keyboard.type(taskParam.value);
|
876
|
-
} else {
|
877
|
-
await this.page.keyboard.type(taskParam.value);
|
878
902
|
}
|
903
|
+
await this.page.keyboard.type(taskParam.value, {
|
904
|
+
autoDismissKeyboard: taskParam.autoDismissKeyboard
|
905
|
+
});
|
879
906
|
}
|
880
907
|
};
|
881
908
|
tasks.push(taskActionInput);
|
@@ -904,6 +931,22 @@ var PageTaskExecutor = class {
|
|
904
931
|
}
|
905
932
|
};
|
906
933
|
tasks.push(taskActionTap);
|
934
|
+
} else if (plan2.type === "RightClick") {
|
935
|
+
const taskActionRightClick = {
|
936
|
+
type: "Action",
|
937
|
+
subType: "RightClick",
|
938
|
+
thought: plan2.thought,
|
939
|
+
locate: plan2.locate,
|
940
|
+
executor: async (param, { element }) => {
|
941
|
+
(0, import_utils6.assert)(element, "Element not found, cannot right click");
|
942
|
+
await this.page.mouse.click(
|
943
|
+
element.center[0],
|
944
|
+
element.center[1],
|
945
|
+
{ button: "right" }
|
946
|
+
);
|
947
|
+
}
|
948
|
+
};
|
949
|
+
tasks.push(taskActionRightClick);
|
907
950
|
} else if (plan2.type === "Drag") {
|
908
951
|
const taskActionDrag = {
|
909
952
|
type: "Action",
|
@@ -1432,7 +1475,7 @@ var PageTaskExecutor = class {
|
|
1432
1475
|
executor: taskExecutor
|
1433
1476
|
};
|
1434
1477
|
}
|
1435
|
-
async createTypeQueryTask(type, demand) {
|
1478
|
+
async createTypeQueryTask(type, demand, opt) {
|
1436
1479
|
const taskExecutor = new import_misoai_core.Executor(
|
1437
1480
|
taskTitleStr(
|
1438
1481
|
type,
|
@@ -1463,7 +1506,10 @@ var PageTaskExecutor = class {
|
|
1463
1506
|
result: `${type}, ${demand}`
|
1464
1507
|
};
|
1465
1508
|
}
|
1466
|
-
const { data, usage } = await this.insight.extract(
|
1509
|
+
const { data, usage } = await this.insight.extract(
|
1510
|
+
demandInput,
|
1511
|
+
opt
|
1512
|
+
);
|
1467
1513
|
let outputResult = data;
|
1468
1514
|
if (ifTypeRestricted) {
|
1469
1515
|
(0, import_utils6.assert)(data?.result !== void 0, "No result in query data");
|
@@ -1483,17 +1529,17 @@ var PageTaskExecutor = class {
|
|
1483
1529
|
executor: taskExecutor
|
1484
1530
|
};
|
1485
1531
|
}
|
1486
|
-
async query(demand) {
|
1487
|
-
return this.createTypeQueryTask("Query", demand);
|
1532
|
+
async query(demand, opt) {
|
1533
|
+
return this.createTypeQueryTask("Query", demand, opt);
|
1488
1534
|
}
|
1489
|
-
async boolean(prompt) {
|
1490
|
-
return this.createTypeQueryTask("Boolean", prompt);
|
1535
|
+
async boolean(prompt, opt) {
|
1536
|
+
return this.createTypeQueryTask("Boolean", prompt, opt);
|
1491
1537
|
}
|
1492
|
-
async number(prompt) {
|
1493
|
-
return this.createTypeQueryTask("Number", prompt);
|
1538
|
+
async number(prompt, opt) {
|
1539
|
+
return this.createTypeQueryTask("Number", prompt, opt);
|
1494
1540
|
}
|
1495
|
-
async string(prompt) {
|
1496
|
-
return this.createTypeQueryTask("String", prompt);
|
1541
|
+
async string(prompt, opt) {
|
1542
|
+
return this.createTypeQueryTask("String", prompt, opt);
|
1497
1543
|
}
|
1498
1544
|
async assert(assertion) {
|
1499
1545
|
const description = `assert: ${assertion}`;
|
@@ -1629,7 +1675,7 @@ function buildPlans(type, locateParam, param) {
|
|
1629
1675
|
param: locateParam,
|
1630
1676
|
thought: ""
|
1631
1677
|
} : null;
|
1632
|
-
if (type === "Tap" || type === "Hover") {
|
1678
|
+
if (type === "Tap" || type === "Hover" || type === "RightClick") {
|
1633
1679
|
(0, import_utils8.assert)(locateParam, `missing locate info for action "${type}"`);
|
1634
1680
|
(0, import_utils8.assert)(locatePlan, `missing locate info for action "${type}"`);
|
1635
1681
|
const tapPlan = {
|
@@ -1709,7 +1755,7 @@ var import_js_yaml3 = __toESM(require("js-yaml"));
|
|
1709
1755
|
var import_semver = __toESM(require("semver"));
|
1710
1756
|
|
1711
1757
|
// package.json
|
1712
|
-
var version = "1.
|
1758
|
+
var version = "1.0.3";
|
1713
1759
|
|
1714
1760
|
// src/common/task-cache.ts
|
1715
1761
|
var debug3 = (0, import_logger3.getDebug)("cache");
|
@@ -1840,8 +1886,14 @@ cache file: ${cacheFile}`
|
|
1840
1886
|
return;
|
1841
1887
|
}
|
1842
1888
|
try {
|
1889
|
+
const dir = (0, import_node_path2.dirname)(this.cacheFilePath);
|
1890
|
+
if (!(0, import_node_fs2.existsSync)(dir)) {
|
1891
|
+
(0, import_node_fs2.mkdirSync)(dir, { recursive: true });
|
1892
|
+
debug3("created cache directory: %s", dir);
|
1893
|
+
}
|
1843
1894
|
const yamlData = import_js_yaml3.default.dump(this.cache);
|
1844
1895
|
(0, import_node_fs2.writeFileSync)(this.cacheFilePath, yamlData);
|
1896
|
+
debug3("cache flushed to file: %s", this.cacheFilePath);
|
1845
1897
|
} catch (err) {
|
1846
1898
|
debug3(
|
1847
1899
|
"write cache to file failed, path: %s, error: %s",
|
@@ -2099,6 +2151,23 @@ var PageAgent = class {
|
|
2099
2151
|
metadata
|
2100
2152
|
};
|
2101
2153
|
}
|
2154
|
+
async aiRightClick(locatePrompt, opt) {
|
2155
|
+
const detailedLocateParam = this.buildDetailedLocateParam(
|
2156
|
+
locatePrompt,
|
2157
|
+
opt
|
2158
|
+
);
|
2159
|
+
const plans = buildPlans("RightClick", detailedLocateParam);
|
2160
|
+
const { executor, output } = await this.taskExecutor.runPlans(
|
2161
|
+
taskTitleStr("RightClick", locateParamStr(detailedLocateParam)),
|
2162
|
+
plans,
|
2163
|
+
{ cacheable: opt?.cacheable }
|
2164
|
+
);
|
2165
|
+
const metadata = this.afterTaskRunning(executor);
|
2166
|
+
return {
|
2167
|
+
result: output,
|
2168
|
+
metadata
|
2169
|
+
};
|
2170
|
+
}
|
2102
2171
|
async aiInput(value, locatePrompt, opt) {
|
2103
2172
|
(0, import_utils12.assert)(
|
2104
2173
|
typeof value === "string",
|
@@ -2530,6 +2599,40 @@ ${errors}`);
|
|
2530
2599
|
}
|
2531
2600
|
throw new Error("evaluateJavaScript is not supported in current agent");
|
2532
2601
|
}
|
2602
|
+
async logScreenshot(title, options) {
|
2603
|
+
const screenshotTitle = title || "untitled";
|
2604
|
+
const content = options?.content || "";
|
2605
|
+
const screenshot = await this.page.screenshotBase64?.();
|
2606
|
+
if (screenshot) {
|
2607
|
+
const executionDump = {
|
2608
|
+
name: screenshotTitle,
|
2609
|
+
description: content,
|
2610
|
+
tasks: [{
|
2611
|
+
type: "Screenshot",
|
2612
|
+
subType: "log",
|
2613
|
+
status: "finished",
|
2614
|
+
executor: null,
|
2615
|
+
param: {
|
2616
|
+
title: screenshotTitle,
|
2617
|
+
content
|
2618
|
+
},
|
2619
|
+
output: {
|
2620
|
+
screenshot
|
2621
|
+
},
|
2622
|
+
thought: `Logged screenshot: ${screenshotTitle}`,
|
2623
|
+
timing: {
|
2624
|
+
start: Date.now(),
|
2625
|
+
end: Date.now(),
|
2626
|
+
cost: 0
|
2627
|
+
}
|
2628
|
+
}],
|
2629
|
+
sdkVersion: "1.0.0",
|
2630
|
+
logTime: Date.now(),
|
2631
|
+
model_name: "screenshot"
|
2632
|
+
};
|
2633
|
+
this.appendExecutionDump(executionDump);
|
2634
|
+
}
|
2635
|
+
}
|
2533
2636
|
async destroy() {
|
2534
2637
|
await this.page.destroy();
|
2535
2638
|
}
|
@@ -2953,6 +3056,14 @@ var PlaywrightAiFixture = (options) => {
|
|
2953
3056
|
aiActionType: "aiTap"
|
2954
3057
|
});
|
2955
3058
|
},
|
3059
|
+
aiRightClick: async ({ page }, use, testInfo) => {
|
3060
|
+
await generateAiFunction({
|
3061
|
+
page,
|
3062
|
+
testInfo,
|
3063
|
+
use,
|
3064
|
+
aiActionType: "aiRightClick"
|
3065
|
+
});
|
3066
|
+
},
|
2956
3067
|
aiHover: async ({ page }, use, testInfo) => {
|
2957
3068
|
await generateAiFunction({
|
2958
3069
|
page,
|
@@ -3040,6 +3151,14 @@ var PlaywrightAiFixture = (options) => {
|
|
3040
3151
|
use,
|
3041
3152
|
aiActionType: "aiBoolean"
|
3042
3153
|
});
|
3154
|
+
},
|
3155
|
+
logScreenshot: async ({ page }, use, testInfo) => {
|
3156
|
+
await generateAiFunction({
|
3157
|
+
page,
|
3158
|
+
testInfo,
|
3159
|
+
use,
|
3160
|
+
aiActionType: "logScreenshot"
|
3161
|
+
});
|
3043
3162
|
}
|
3044
3163
|
};
|
3045
3164
|
};
|