@midscene/core 1.5.8 → 1.6.0
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/agent.mjs +21 -6
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/dump/html-utils.mjs +74 -1
- package/dist/es/dump/html-utils.mjs.map +1 -1
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/report-generator.mjs +51 -23
- package/dist/es/report-generator.mjs.map +1 -1
- package/dist/es/report.mjs +29 -3
- package/dist/es/report.mjs.map +1 -1
- package/dist/es/task-runner.mjs +3 -0
- package/dist/es/task-runner.mjs.map +1 -1
- package/dist/es/types.mjs +3 -0
- package/dist/es/types.mjs.map +1 -1
- package/dist/es/utils.mjs +15 -4
- package/dist/es/utils.mjs.map +1 -1
- package/dist/es/yaml/utils.mjs +24 -1
- package/dist/es/yaml/utils.mjs.map +1 -1
- package/dist/lib/agent/agent.js +21 -6
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/dump/html-utils.js +79 -3
- package/dist/lib/dump/html-utils.js.map +1 -1
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/report-generator.js +49 -21
- package/dist/lib/report-generator.js.map +1 -1
- package/dist/lib/report.js +27 -1
- package/dist/lib/report.js.map +1 -1
- package/dist/lib/task-runner.js +3 -0
- package/dist/lib/task-runner.js.map +1 -1
- package/dist/lib/types.js +3 -0
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/utils.js +15 -4
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/yaml/utils.js +24 -1
- package/dist/lib/yaml/utils.js.map +1 -1
- package/dist/types/agent/agent.d.ts +3 -1
- package/dist/types/dump/html-utils.d.ts +11 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/report-generator.d.ts +31 -13
- package/dist/types/report.d.ts +7 -0
- package/dist/types/task-runner.d.ts +1 -0
- package/dist/types/types.d.ts +10 -0
- package/dist/types/yaml.d.ts +3 -3
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dump/html-utils.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/dump/html-utils.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { closeSync, openSync, readSync, statSync } from 'node:fs';\nimport { antiEscapeScriptTag, escapeScriptTag } from '@midscene/shared/utils';\n\nexport const escapeContent = escapeScriptTag;\nexport const unescapeContent = antiEscapeScriptTag;\n\n/** Chunk size for streaming file operations (64KB) */\nexport const STREAMING_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Callback for processing matched tags during streaming.\n * @param content - The content between open and close tags\n * @returns true to stop scanning, false to continue\n */\ntype TagMatchCallback = (content: string) => boolean;\n\n/**\n * Stream through a file and find tags matching the pattern.\n * Memory usage: O(chunk_size + tag_size), not O(file_size).\n *\n * @param filePath - Absolute path to the file\n * @param openTag - Opening tag to search for\n * @param closeTag - Closing tag\n * @param onMatch - Callback for each matched tag content\n */\nexport function streamScanTags(\n filePath: string,\n openTag: string,\n closeTag: string,\n onMatch: TagMatchCallback,\n): void {\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTag, searchStart);\n if (startIdx !== -1) {\n capturing = true;\n currentContent = chunk.slice(startIdx + openTag.length);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n const shouldStop = onMatch(currentContent.slice(0, endIdx));\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n searchStart =\n startIdx + openTag.length + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(-openTag.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n const shouldStop = onMatch(currentContent);\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n}\n\n/**\n * Synchronously extract a specific image's base64 data from an HTML file by its id.\n * Uses streaming to avoid loading the entire file into memory.\n *\n * @param htmlPath - Absolute path to the HTML file\n * @param imageId - The id of the image to extract\n * @returns The base64 data string, or null if not found\n */\nexport function extractImageByIdSync(\n htmlPath: string,\n imageId: string,\n): string | null {\n const targetTag = `<script type=\"midscene-image\" data-id=\"${imageId}\">`;\n const closeTag = '</script>';\n\n let result: string | null = null;\n\n streamScanTags(htmlPath, targetTag, closeTag, (content) => {\n result = unescapeContent(content);\n return true; // Stop after first match\n });\n\n return result;\n}\n\n/**\n * Stream image script tags from source file directly to output file.\n * Memory usage: O(single_image_size), not O(all_images_size).\n *\n * @param srcFilePath - Source HTML file path\n * @param destFilePath - Destination file path to append to\n */\nexport function streamImageScriptsToFile(\n srcFilePath: string,\n destFilePath: string,\n): void {\n const { appendFileSync } = require('node:fs');\n const openTag = '<script type=\"midscene-image\"';\n const closeTag = '</script>';\n\n streamScanTags(srcFilePath, openTag, closeTag, (content) => {\n // Write complete tag immediately to destination, don't accumulate\n appendFileSync(destFilePath, `${openTag}${content}${closeTag}\\n`);\n return false; // Continue scanning for more tags\n });\n}\n\n/**\n * Extract the LAST dump script content from HTML file using streaming.\n * Memory usage: O(dump_size), not O(file_size).\n *\n * @param filePath - Absolute path to the HTML file\n * @returns The dump script content (trimmed), or empty string if not found\n */\nexport function extractLastDumpScriptSync(filePath: string): string {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = '</script>';\n\n let lastContent = '';\n\n // Custom streaming to handle the special case where open tag has variable attributes\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n // Find the end of the opening tag (the '>' character)\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n lastContent = currentContent.slice(0, endIdx).trim();\n capturing = false;\n currentContent = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n lastContent = currentContent.trim();\n capturing = false;\n currentContent = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n\n return lastContent;\n}\n\nexport function parseImageScripts(html: string): Record<string, string> {\n const imageMap: Record<string, string> = {};\n const regex =\n /<script type=\"midscene-image\" data-id=\"([^\"]+)\">([\\s\\S]*?)<\\/script>/g;\n\n for (const match of html.matchAll(regex)) {\n const [, id, content] = match;\n imageMap[id] = unescapeContent(content);\n }\n\n return imageMap;\n}\n\nexport function parseDumpScript(html: string): string {\n // Use string search instead of regex to avoid ReDoS vulnerability\n // Find the LAST dump script tag (template may contain similar patterns in bundled JS)\n const scriptOpenTag = '<script type=\"midscene_web_dump\"';\n const scriptCloseTag = '</script>';\n\n // Find the last occurrence of the opening tag\n const lastOpenIndex = html.lastIndexOf(scriptOpenTag);\n if (lastOpenIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the end of the opening tag (the '>' character)\n const tagEndIndex = html.indexOf('>', lastOpenIndex);\n if (tagEndIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the closing tag after the opening tag\n const closeIndex = html.indexOf(scriptCloseTag, tagEndIndex);\n if (closeIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Extract content between opening and closing tags\n const content = html.substring(tagEndIndex + 1, closeIndex);\n return unescapeContent(content);\n}\n\nexport function parseDumpScriptAttributes(\n html: string,\n): Record<string, string> {\n const regex = /<script type=\"midscene_web_dump\"([^>]*)>/;\n const match = regex.exec(html);\n\n if (!match) {\n return {};\n }\n\n const attrString = match[1];\n const attributes: Record<string, string> = {};\n const attrRegex = /(\\w+)=\"([^\"]*)\"/g;\n\n for (const attrMatch of attrString.matchAll(attrRegex)) {\n const [, key, value] = attrMatch;\n if (key !== 'type') {\n attributes[key] = decodeURIComponent(value);\n }\n }\n\n return attributes;\n}\n\nexport function generateImageScriptTag(id: string, data: string): string {\n // Do not use template string here, will cause bundle error with <script\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene-image\" data-id=\"' +\n id +\n '\">' +\n escapeContent(data) +\n '</script>'\n );\n}\n\n/**\n * Inline script that fixes relative URL resolution for directory-mode reports.\n *\n * Problem: when a static server (e.g. `npx serve`) serves `name/index.html`\n * at URL `/name` (without trailing slash), relative paths like\n * `./screenshots/xxx.png` resolve to `/screenshots/xxx.png` instead of\n * `/name/screenshots/xxx.png`.\n *\n * Fix: dynamically insert a <base> tag so relative URLs resolve correctly.\n */\n// Do not use template string here, will cause bundle error with <script\n//\n// The closing </script> tag is built at runtime via scriptClose() so that no\n// bundler (rslib, webpack, rsbuild) can ever see or inline a literal\n// '</script>' into JS source. A literal '</script>' inside a <script> block\n// causes the HTML parser to prematurely close the block — which breaks the\n// report viewer when this module is bundled into the report HTML template.\n//\n// Do NOT replace this with a string constant, hex escape (\\x3c), or simple\n// concatenation — bundlers will optimise / inline them and re-introduce the\n// literal '</script>'.\nlet _baseUrlFixScript: string;\nexport function getBaseUrlFixScript(): string {\n if (!_baseUrlFixScript) {\n // Closing </script> MUST be split so that no bundler (rslib / webpack /\n // terser) can ever produce a literal '</script>' in bundle output.\n // A literal '</script>' inside a <script> block causes the HTML parser\n // to prematurely close the block, which breaks the report viewer when\n // this module is bundled into the report template.\n const close = '</' + 'script>';\n _baseUrlFixScript =\n // biome-ignore lint/style/useTemplate: see above\n '\\n<script>(function(){' +\n 'var p=window.location.pathname;' +\n 'if(p.endsWith(\"/\")||/\\\\.\\\\w+$/.test(p))return;' +\n 'var b=document.createElement(\"base\");' +\n 'b.href=p+\"/\";' +\n 'document.head.insertBefore(b,document.head.firstChild)' +\n '})()' +\n close +\n '\\n';\n }\n return _baseUrlFixScript;\n}\n\nexport function generateDumpScriptTag(\n json: string,\n attributes?: Record<string, string>,\n): string {\n let attrString = '';\n if (attributes && Object.keys(attributes).length > 0) {\n // Do not use template string here, will cause bundle error with <script\n attrString =\n // biome-ignore lint/style/useTemplate: <explanation>\n ' ' +\n Object.entries(attributes)\n // biome-ignore lint/style/useTemplate: <explanation>\n .map(([k, v]) => k + '=\"' + encodeURIComponent(v) + '\"')\n .join(' ');\n }\n\n // Do not use template string here, will cause bundle error with <script\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene_web_dump\"' +\n attrString +\n '>' +\n escapeContent(json) +\n '</script>'\n );\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","escapeContent","escapeScriptTag","unescapeContent","antiEscapeScriptTag","STREAMING_CHUNK_SIZE","streamScanTags","filePath","openTag","closeTag","onMatch","fd","openSync","fileSize","statSync","buffer","Buffer","position","leftover","capturing","currentContent","bytesRead","readSync","chunk","searchStart","endIdx","shouldStop","startIdx","closeSync","extractImageByIdSync","htmlPath","imageId","targetTag","result","content","streamImageScriptsToFile","srcFilePath","destFilePath","appendFileSync","require","extractLastDumpScriptSync","openTagPrefix","lastContent","tagEndIdx","parseImageScripts","html","imageMap","regex","match","id","parseDumpScript","scriptOpenTag","scriptCloseTag","lastOpenIndex","Error","tagEndIndex","closeIndex","parseDumpScriptAttributes","attrString","attributes","attrRegex","attrMatch","value","decodeURIComponent","generateImageScriptTag","data","_baseUrlFixScript","getBaseUrlFixScript","close","generateDumpScriptTag","json","k","v","encodeURIComponent"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dump/html-utils.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/dump/html-utils.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { closeSync, openSync, readSync, statSync } from 'node:fs';\nimport { antiEscapeScriptTag, escapeScriptTag } from '@midscene/shared/utils';\n\nexport const escapeContent = escapeScriptTag;\nexport const unescapeContent = antiEscapeScriptTag;\n\n/** Chunk size for streaming file operations (64KB) */\nexport const STREAMING_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Callback for processing matched tags during streaming.\n * @param content - The content between open and close tags\n * @returns true to stop scanning, false to continue\n */\ntype TagMatchCallback = (content: string) => boolean;\n\n/**\n * Stream through a file and find tags matching the pattern.\n * Memory usage: O(chunk_size + tag_size), not O(file_size).\n *\n * @param filePath - Absolute path to the file\n * @param openTag - Opening tag to search for\n * @param closeTag - Closing tag\n * @param onMatch - Callback for each matched tag content\n */\nexport function streamScanTags(\n filePath: string,\n openTag: string,\n closeTag: string,\n onMatch: TagMatchCallback,\n): void {\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTag, searchStart);\n if (startIdx !== -1) {\n capturing = true;\n currentContent = chunk.slice(startIdx + openTag.length);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n const shouldStop = onMatch(currentContent.slice(0, endIdx));\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n searchStart =\n startIdx + openTag.length + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(-openTag.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n const shouldStop = onMatch(currentContent);\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n}\n\n/**\n * Synchronously extract a specific image's base64 data from an HTML file by its id.\n * Uses streaming to avoid loading the entire file into memory.\n *\n * @param htmlPath - Absolute path to the HTML file\n * @param imageId - The id of the image to extract\n * @returns The base64 data string, or null if not found\n */\nexport function extractImageByIdSync(\n htmlPath: string,\n imageId: string,\n): string | null {\n const targetTag = `<script type=\"midscene-image\" data-id=\"${imageId}\">`;\n const closeTag = '</script>';\n\n let result: string | null = null;\n\n streamScanTags(htmlPath, targetTag, closeTag, (content) => {\n result = unescapeContent(content);\n return true; // Stop after first match\n });\n\n return result;\n}\n\n/**\n * Stream image script tags from source file directly to output file.\n * Memory usage: O(single_image_size), not O(all_images_size).\n *\n * @param srcFilePath - Source HTML file path\n * @param destFilePath - Destination file path to append to\n */\nexport function streamImageScriptsToFile(\n srcFilePath: string,\n destFilePath: string,\n): void {\n const { appendFileSync } = require('node:fs');\n const openTag = '<script type=\"midscene-image\"';\n const closeTag = '</script>';\n\n streamScanTags(srcFilePath, openTag, closeTag, (content) => {\n // Write complete tag immediately to destination, don't accumulate\n appendFileSync(destFilePath, `${openTag}${content}${closeTag}\\n`);\n return false; // Continue scanning for more tags\n });\n}\n\n/**\n * Extract the LAST dump script content from HTML file using streaming.\n * Memory usage: O(dump_size), not O(file_size).\n *\n * @param filePath - Absolute path to the HTML file\n * @returns The dump script content (trimmed), or empty string if not found\n */\nexport function extractLastDumpScriptSync(filePath: string): string {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = '</script>';\n\n let lastContent = '';\n\n // Custom streaming to handle the special case where open tag has variable attributes\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n // Find the end of the opening tag (the '>' character)\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n lastContent = currentContent.slice(0, endIdx).trim();\n capturing = false;\n currentContent = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n lastContent = currentContent.trim();\n capturing = false;\n currentContent = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n\n return lastContent;\n}\n\n/**\n * Extract ALL dump script contents from an HTML file using streaming.\n * Each entry includes the full opening tag (for attribute extraction) and the content.\n *\n * @param filePath - Absolute path to the HTML file\n * @returns Array of { openTag, content } for each dump script found\n */\nexport function extractAllDumpScriptsSync(\n filePath: string,\n): { openTag: string; content: string }[] {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = '</script>';\n\n const results: { openTag: string; content: string }[] = [];\n\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n let currentOpenTag = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentOpenTag = chunk.slice(startIdx, tagEndIdx + 1);\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n results.push({\n openTag: currentOpenTag,\n content: currentContent.slice(0, endIdx).trim(),\n });\n capturing = false;\n currentContent = '';\n currentOpenTag = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n results.push({\n openTag: currentOpenTag,\n content: currentContent.trim(),\n });\n capturing = false;\n currentContent = '';\n currentOpenTag = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n\n return results;\n}\n\nexport function parseImageScripts(html: string): Record<string, string> {\n const imageMap: Record<string, string> = {};\n const regex =\n /<script type=\"midscene-image\" data-id=\"([^\"]+)\">([\\s\\S]*?)<\\/script>/g;\n\n for (const match of html.matchAll(regex)) {\n const [, id, content] = match;\n imageMap[id] = unescapeContent(content);\n }\n\n return imageMap;\n}\n\nexport function parseDumpScript(html: string): string {\n // Use string search instead of regex to avoid ReDoS vulnerability\n // Find the LAST dump script tag (template may contain similar patterns in bundled JS)\n const scriptOpenTag = '<script type=\"midscene_web_dump\"';\n const scriptCloseTag = '</script>';\n\n // Find the last occurrence of the opening tag\n const lastOpenIndex = html.lastIndexOf(scriptOpenTag);\n if (lastOpenIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the end of the opening tag (the '>' character)\n const tagEndIndex = html.indexOf('>', lastOpenIndex);\n if (tagEndIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the closing tag after the opening tag\n const closeIndex = html.indexOf(scriptCloseTag, tagEndIndex);\n if (closeIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Extract content between opening and closing tags\n const content = html.substring(tagEndIndex + 1, closeIndex);\n return unescapeContent(content);\n}\n\nexport function parseDumpScriptAttributes(\n html: string,\n): Record<string, string> {\n const regex = /<script type=\"midscene_web_dump\"([^>]*)>/;\n const match = regex.exec(html);\n\n if (!match) {\n return {};\n }\n\n const attrString = match[1];\n const attributes: Record<string, string> = {};\n const attrRegex = /(\\w+)=\"([^\"]*)\"/g;\n\n for (const attrMatch of attrString.matchAll(attrRegex)) {\n const [, key, value] = attrMatch;\n if (key !== 'type') {\n attributes[key] = decodeURIComponent(value);\n }\n }\n\n return attributes;\n}\n\nexport function generateImageScriptTag(id: string, data: string): string {\n // Do not use template string here, will cause bundle error with <script\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene-image\" data-id=\"' +\n id +\n '\">' +\n escapeContent(data) +\n '</script>'\n );\n}\n\n/**\n * Inline script that fixes relative URL resolution for directory-mode reports.\n *\n * Problem: when a static server (e.g. `npx serve`) serves `name/index.html`\n * at URL `/name` (without trailing slash), relative paths like\n * `./screenshots/xxx.png` resolve to `/screenshots/xxx.png` instead of\n * `/name/screenshots/xxx.png`.\n *\n * Fix: dynamically insert a <base> tag so relative URLs resolve correctly.\n */\n// Do not use template string here, will cause bundle error with <script\n//\n// The closing </script> tag is built at runtime via scriptClose() so that no\n// bundler (rslib, webpack, rsbuild) can ever see or inline a literal\n// '</script>' into JS source. A literal '</script>' inside a <script> block\n// causes the HTML parser to prematurely close the block — which breaks the\n// report viewer when this module is bundled into the report HTML template.\n//\n// Do NOT replace this with a string constant, hex escape (\\x3c), or simple\n// concatenation — bundlers will optimise / inline them and re-introduce the\n// literal '</script>'.\nlet _baseUrlFixScript: string;\nexport function getBaseUrlFixScript(): string {\n if (!_baseUrlFixScript) {\n // Closing </script> MUST be split so that no bundler (rslib / webpack /\n // terser) can ever produce a literal '</script>' in bundle output.\n // A literal '</script>' inside a <script> block causes the HTML parser\n // to prematurely close the block, which breaks the report viewer when\n // this module is bundled into the report template.\n const close = '</' + 'script>';\n _baseUrlFixScript =\n // biome-ignore lint/style/useTemplate: see above\n '\\n<script>(function(){' +\n 'var p=window.location.pathname;' +\n 'if(p.endsWith(\"/\")||/\\\\.\\\\w+$/.test(p))return;' +\n 'var b=document.createElement(\"base\");' +\n 'b.href=p+\"/\";' +\n 'document.head.insertBefore(b,document.head.firstChild)' +\n '})()' +\n close +\n '\\n';\n }\n return _baseUrlFixScript;\n}\n\nexport function generateDumpScriptTag(\n json: string,\n attributes?: Record<string, string>,\n): string {\n let attrString = '';\n if (attributes && Object.keys(attributes).length > 0) {\n // Do not use template string here, will cause bundle error with <script\n attrString =\n // biome-ignore lint/style/useTemplate: <explanation>\n ' ' +\n Object.entries(attributes)\n // biome-ignore lint/style/useTemplate: <explanation>\n .map(([k, v]) => k + '=\"' + encodeURIComponent(v) + '\"')\n .join(' ');\n }\n\n // Do not use template string here, will cause bundle error with <script\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene_web_dump\"' +\n attrString +\n '>' +\n escapeContent(json) +\n '</script>'\n );\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","escapeContent","escapeScriptTag","unescapeContent","antiEscapeScriptTag","STREAMING_CHUNK_SIZE","streamScanTags","filePath","openTag","closeTag","onMatch","fd","openSync","fileSize","statSync","buffer","Buffer","position","leftover","capturing","currentContent","bytesRead","readSync","chunk","searchStart","endIdx","shouldStop","startIdx","closeSync","extractImageByIdSync","htmlPath","imageId","targetTag","result","content","streamImageScriptsToFile","srcFilePath","destFilePath","appendFileSync","require","extractLastDumpScriptSync","openTagPrefix","lastContent","tagEndIdx","extractAllDumpScriptsSync","results","currentOpenTag","parseImageScripts","html","imageMap","regex","match","id","parseDumpScript","scriptOpenTag","scriptCloseTag","lastOpenIndex","Error","tagEndIndex","closeIndex","parseDumpScriptAttributes","attrString","attributes","attrRegex","attrMatch","value","decodeURIComponent","generateImageScriptTag","data","_baseUrlFixScript","getBaseUrlFixScript","close","generateDumpScriptTag","json","k","v","encodeURIComponent"],"mappings":"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"}
|
package/dist/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/index.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { z } from 'zod';\nimport Service from './service/index';\nimport { TaskRunner } from './task-runner';\nimport { getVersion } from './utils';\n\nexport {\n plan,\n AiLocateElement,\n getMidsceneLocationSchema,\n PointSchema,\n SizeSchema,\n RectSchema,\n TMultimodalPromptSchema,\n TUserPromptSchema,\n type TMultimodalPrompt,\n type TUserPrompt,\n} from './ai-model/index';\n\nexport {\n MIDSCENE_MODEL_NAME,\n type CreateOpenAIClientFn,\n} from '@midscene/shared/env';\n\nexport type * from './types';\nexport {\n ServiceError,\n ExecutionDump,\n GroupedActionDump,\n type IExecutionDump,\n type IGroupedActionDump,\n} from './types';\n\nexport { z };\n\nexport default Service;\nexport { TaskRunner, Service, getVersion };\n\nexport type {\n MidsceneYamlScript,\n MidsceneYamlTask,\n MidsceneYamlFlowItem,\n MidsceneYamlConfigResult,\n MidsceneYamlConfig,\n MidsceneYamlScriptWebEnv,\n MidsceneYamlScriptAndroidEnv,\n MidsceneYamlScriptIOSEnv,\n MidsceneYamlScriptEnv,\n LocateOption,\n DetailedLocateParam,\n} from './yaml';\n\nexport { Agent, type AgentOpt, type AiActOptions, createAgent } from './agent';\n\n// Dump utilities\nexport {\n restoreImageReferences,\n escapeContent,\n unescapeContent,\n parseImageScripts,\n parseDumpScript,\n parseDumpScriptAttributes,\n generateImageScriptTag,\n generateDumpScriptTag,\n} from './dump';\n\n// Report generator\nexport type { IReportGenerator } from './report-generator';\nexport { ReportGenerator, nullReportGenerator } from './report-generator';\n\n// ScreenshotItem\nexport { ScreenshotItem } from './screenshot-item';\n"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","Service"],"mappings":";;;IACAA,oBAAoB,CAAC,GAAG,CAACC;QACxB,IAAIC,SAASD,UAAUA,OAAO,UAAU,GACvC,IAAOA,MAAM,CAAC,UAAU,GACxB,IAAOA;QACRD,oBAAoB,CAAC,CAACE,QAAQ;YAAE,GAAGA;QAAO;QAC1C,OAAOA;IACR;;;ICPAF,oBAAoB,CAAC,GAAG,CAAC,UAASG;QACjC,IAAI,IAAIC,OAAOD,WACR,IAAGH,oBAAoB,CAAC,CAACG,YAAYC,QAAQ,CAACJ,oBAAoB,CAAC,CAAC,UAASI,MACzEC,OAAO,cAAc,CAAC,UAASD,KAAK;YAAE,YAAY;YAAM,KAAKD,UAAU,CAACC,IAAI;QAAC;IAGzF;;;ICNAJ,oBAAoB,CAAC,GAAG,CAACM,KAAKC,OAAUF,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACC,KAAKC;;;ICClFP,oBAAoB,CAAC,GAAG,CAAC;QACxB,IAAG,AAAkB,eAAlB,OAAOQ,UAA0BA,OAAO,WAAW,EACrDH,OAAO,cAAc,CAAC,UAASG,OAAO,WAAW,EAAE;YAAE,OAAO;QAAS;QAEtEH,OAAO,cAAc,CAAC,UAAS,cAAc;YAAE,OAAO;QAAK;IAC5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.js","sources":["webpack/runtime/compat_get_default_export","webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/index.ts"],"sourcesContent":["// getDefaultExport function for compatibility with non-ESM modules\n__webpack_require__.n = (module) => {\n\tvar getter = module && module.__esModule ?\n\t\t() => (module['default']) :\n\t\t() => (module);\n\t__webpack_require__.d(getter, { a: getter });\n\treturn getter;\n};\n","__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { z } from 'zod';\nimport Service from './service/index';\nimport { TaskRunner } from './task-runner';\nimport { getVersion } from './utils';\n\nexport {\n plan,\n AiLocateElement,\n getMidsceneLocationSchema,\n PointSchema,\n SizeSchema,\n RectSchema,\n TMultimodalPromptSchema,\n TUserPromptSchema,\n type TMultimodalPrompt,\n type TUserPrompt,\n} from './ai-model/index';\n\nexport {\n MIDSCENE_MODEL_NAME,\n type CreateOpenAIClientFn,\n} from '@midscene/shared/env';\n\nexport type * from './types';\nexport {\n ServiceError,\n ExecutionDump,\n GroupedActionDump,\n type IExecutionDump,\n type IGroupedActionDump,\n type GroupMeta,\n} from './types';\n\nexport { z };\n\nexport default Service;\nexport { TaskRunner, Service, getVersion };\n\nexport type {\n MidsceneYamlScript,\n MidsceneYamlTask,\n MidsceneYamlFlowItem,\n MidsceneYamlConfigResult,\n MidsceneYamlConfig,\n MidsceneYamlScriptWebEnv,\n MidsceneYamlScriptAndroidEnv,\n MidsceneYamlScriptIOSEnv,\n MidsceneYamlScriptEnv,\n LocateOption,\n DetailedLocateParam,\n} from './yaml';\n\nexport { Agent, type AgentOpt, type AiActOptions, createAgent } from './agent';\n\n// Dump utilities\nexport {\n restoreImageReferences,\n escapeContent,\n unescapeContent,\n parseImageScripts,\n parseDumpScript,\n parseDumpScriptAttributes,\n generateImageScriptTag,\n generateDumpScriptTag,\n} from './dump';\n\n// Report generator\nexport type { IReportGenerator } from './report-generator';\nexport { ReportGenerator, nullReportGenerator } from './report-generator';\n\n// ScreenshotItem\nexport { ScreenshotItem } from './screenshot-item';\n"],"names":["__webpack_require__","module","getter","definition","key","Object","obj","prop","Symbol","Service"],"mappings":";;;IACAA,oBAAoB,CAAC,GAAG,CAACC;QACxB,IAAIC,SAASD,UAAUA,OAAO,UAAU,GACvC,IAAOA,MAAM,CAAC,UAAU,GACxB,IAAOA;QACRD,oBAAoB,CAAC,CAACE,QAAQ;YAAE,GAAGA;QAAO;QAC1C,OAAOA;IACR;;;ICPAF,oBAAoB,CAAC,GAAG,CAAC,UAASG;QACjC,IAAI,IAAIC,OAAOD,WACR,IAAGH,oBAAoB,CAAC,CAACG,YAAYC,QAAQ,CAACJ,oBAAoB,CAAC,CAAC,UAASI,MACzEC,OAAO,cAAc,CAAC,UAASD,KAAK;YAAE,YAAY;YAAM,KAAKD,UAAU,CAACC,IAAI;QAAC;IAGzF;;;ICNAJ,oBAAoB,CAAC,GAAG,CAACM,KAAKC,OAAUF,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACC,KAAKC;;;ICClFP,oBAAoB,CAAC,GAAG,CAAC;QACxB,IAAG,AAAkB,eAAlB,OAAOQ,UAA0BA,OAAO,WAAW,EACrDH,OAAO,cAAc,CAAC,UAASG,OAAO,WAAW,EAAE;YAAE,OAAO;QAAS;QAEtEH,OAAO,cAAc,CAAC,UAAS,cAAc;YAAE,OAAO;QAAK;IAC5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC6BA,YAAeI"}
|
|
@@ -33,6 +33,7 @@ const common_namespaceObject = require("@midscene/shared/common");
|
|
|
33
33
|
const env_namespaceObject = require("@midscene/shared/env");
|
|
34
34
|
const utils_namespaceObject = require("@midscene/shared/utils");
|
|
35
35
|
const html_utils_js_namespaceObject = require("./dump/html-utils.js");
|
|
36
|
+
const external_types_js_namespaceObject = require("./types.js");
|
|
36
37
|
const external_utils_js_namespaceObject = require("./utils.js");
|
|
37
38
|
function _define_property(obj, key, value) {
|
|
38
39
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -45,7 +46,7 @@ function _define_property(obj, key, value) {
|
|
|
45
46
|
return obj;
|
|
46
47
|
}
|
|
47
48
|
const nullReportGenerator = {
|
|
48
|
-
|
|
49
|
+
onExecutionUpdate: ()=>{},
|
|
49
50
|
flush: async ()=>{},
|
|
50
51
|
finalize: async ()=>void 0,
|
|
51
52
|
getReportPath: ()=>void 0
|
|
@@ -68,19 +69,22 @@ class ReportGenerator {
|
|
|
68
69
|
autoPrint: opts.autoPrintReportMsg
|
|
69
70
|
});
|
|
70
71
|
}
|
|
71
|
-
|
|
72
|
+
onExecutionUpdate(execution, groupMeta) {
|
|
73
|
+
this.lastExecution = execution;
|
|
74
|
+
this.lastGroupMeta = groupMeta;
|
|
72
75
|
this.writeQueue = this.writeQueue.then(()=>{
|
|
73
76
|
if (this.destroyed) return;
|
|
74
|
-
this.
|
|
77
|
+
this.doWriteExecution(execution, groupMeta);
|
|
75
78
|
});
|
|
76
79
|
}
|
|
77
80
|
async flush() {
|
|
78
81
|
await this.writeQueue;
|
|
79
82
|
}
|
|
80
|
-
async finalize(
|
|
81
|
-
this.
|
|
83
|
+
async finalize() {
|
|
84
|
+
if (this.lastExecution && this.lastGroupMeta) this.onExecutionUpdate(this.lastExecution, this.lastGroupMeta);
|
|
82
85
|
await this.flush();
|
|
83
86
|
this.destroyed = true;
|
|
87
|
+
if (!this.initialized) return;
|
|
84
88
|
this.printReportPath('finalized');
|
|
85
89
|
return this.reportPath;
|
|
86
90
|
}
|
|
@@ -93,36 +97,49 @@ class ReportGenerator {
|
|
|
93
97
|
if ('directory' === this.screenshotMode) (0, utils_namespaceObject.logMsg)(`Midscene - report ${verb}: npx serve ${(0, external_node_path_namespaceObject.dirname)(this.reportPath)}`);
|
|
94
98
|
else (0, utils_namespaceObject.logMsg)(`Midscene - report ${verb}: ${this.reportPath}`);
|
|
95
99
|
}
|
|
96
|
-
|
|
97
|
-
if ('inline' === this.screenshotMode) this.
|
|
98
|
-
else this.
|
|
100
|
+
doWriteExecution(execution, groupMeta) {
|
|
101
|
+
if ('inline' === this.screenshotMode) this.writeInlineExecution(execution, groupMeta);
|
|
102
|
+
else this.writeDirectoryExecution(execution, groupMeta);
|
|
99
103
|
if (!this.firstWriteDone) {
|
|
100
104
|
this.firstWriteDone = true;
|
|
101
105
|
this.printReportPath('generated');
|
|
102
106
|
}
|
|
103
107
|
}
|
|
104
|
-
|
|
108
|
+
wrapAsGroupedDump(execution, groupMeta) {
|
|
109
|
+
return new external_types_js_namespaceObject.GroupedActionDump({
|
|
110
|
+
sdkVersion: groupMeta.sdkVersion,
|
|
111
|
+
groupName: groupMeta.groupName,
|
|
112
|
+
groupDescription: groupMeta.groupDescription,
|
|
113
|
+
modelBriefs: groupMeta.modelBriefs,
|
|
114
|
+
deviceType: groupMeta.deviceType,
|
|
115
|
+
executions: [
|
|
116
|
+
execution
|
|
117
|
+
]
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
writeInlineExecution(execution, groupMeta) {
|
|
105
121
|
const dir = (0, external_node_path_namespaceObject.dirname)(this.reportPath);
|
|
106
122
|
if (!(0, external_node_fs_namespaceObject.existsSync)(dir)) (0, external_node_fs_namespaceObject.mkdirSync)(dir, {
|
|
107
123
|
recursive: true
|
|
108
124
|
});
|
|
109
125
|
if (!this.initialized) {
|
|
110
126
|
(0, external_node_fs_namespaceObject.writeFileSync)(this.reportPath, (0, external_utils_js_namespaceObject.getReportTpl)());
|
|
111
|
-
this.imageEndOffset = (0, external_node_fs_namespaceObject.statSync)(this.reportPath).size;
|
|
112
127
|
this.initialized = true;
|
|
113
128
|
}
|
|
114
|
-
|
|
115
|
-
const screenshots = dump.collectAllScreenshots();
|
|
129
|
+
const screenshots = execution.collectScreenshots();
|
|
116
130
|
for (const screenshot of screenshots)if (!this.writtenScreenshots.has(screenshot.id)) {
|
|
117
131
|
(0, external_utils_js_namespaceObject.appendFileSync)(this.reportPath, `\n${(0, html_utils_js_namespaceObject.generateImageScriptTag)(screenshot.id, screenshot.base64)}`);
|
|
118
132
|
this.writtenScreenshots.add(screenshot.id);
|
|
119
133
|
screenshot.markPersistedInline(this.reportPath);
|
|
120
134
|
}
|
|
121
|
-
|
|
122
|
-
const serialized =
|
|
123
|
-
|
|
135
|
+
const singleDump = this.wrapAsGroupedDump(execution, groupMeta);
|
|
136
|
+
const serialized = singleDump.serialize();
|
|
137
|
+
const attributes = {
|
|
138
|
+
'data-group-id': this.reportStreamId
|
|
139
|
+
};
|
|
140
|
+
(0, external_utils_js_namespaceObject.appendFileSync)(this.reportPath, `\n${(0, html_utils_js_namespaceObject.generateDumpScriptTag)(serialized, attributes)}`);
|
|
124
141
|
}
|
|
125
|
-
|
|
142
|
+
writeDirectoryExecution(execution, groupMeta) {
|
|
126
143
|
const dir = (0, external_node_path_namespaceObject.dirname)(this.reportPath);
|
|
127
144
|
if (!(0, external_node_fs_namespaceObject.existsSync)(dir)) (0, external_node_fs_namespaceObject.mkdirSync)(dir, {
|
|
128
145
|
recursive: true
|
|
@@ -131,7 +148,7 @@ class ReportGenerator {
|
|
|
131
148
|
if (!(0, external_node_fs_namespaceObject.existsSync)(screenshotsDir)) (0, external_node_fs_namespaceObject.mkdirSync)(screenshotsDir, {
|
|
132
149
|
recursive: true
|
|
133
150
|
});
|
|
134
|
-
const screenshots =
|
|
151
|
+
const screenshots = execution.collectScreenshots();
|
|
135
152
|
for (const screenshot of screenshots)if (!this.writtenScreenshots.has(screenshot.id)) {
|
|
136
153
|
const ext = screenshot.extension;
|
|
137
154
|
const absolutePath = (0, external_node_path_namespaceObject.join)(screenshotsDir, `${screenshot.id}.${ext}`);
|
|
@@ -140,22 +157,33 @@ class ReportGenerator {
|
|
|
140
157
|
this.writtenScreenshots.add(screenshot.id);
|
|
141
158
|
screenshot.markPersistedToPath(`./screenshots/${screenshot.id}.${ext}`, absolutePath);
|
|
142
159
|
}
|
|
143
|
-
const
|
|
144
|
-
|
|
160
|
+
const singleDump = this.wrapAsGroupedDump(execution, groupMeta);
|
|
161
|
+
const serialized = singleDump.serialize();
|
|
162
|
+
const dumpAttributes = {
|
|
163
|
+
'data-group-id': this.reportStreamId
|
|
164
|
+
};
|
|
165
|
+
if (!this.initialized) {
|
|
166
|
+
(0, external_node_fs_namespaceObject.writeFileSync)(this.reportPath, `${(0, external_utils_js_namespaceObject.getReportTpl)()}${(0, html_utils_js_namespaceObject.getBaseUrlFixScript)()}`);
|
|
167
|
+
this.initialized = true;
|
|
168
|
+
}
|
|
169
|
+
(0, external_utils_js_namespaceObject.appendFileSync)(this.reportPath, `\n${(0, html_utils_js_namespaceObject.generateDumpScriptTag)(serialized, dumpAttributes)}`);
|
|
145
170
|
}
|
|
146
171
|
constructor(options){
|
|
147
172
|
_define_property(this, "reportPath", void 0);
|
|
148
173
|
_define_property(this, "screenshotMode", void 0);
|
|
149
174
|
_define_property(this, "autoPrint", void 0);
|
|
150
|
-
_define_property(this, "writtenScreenshots", new Set());
|
|
151
175
|
_define_property(this, "firstWriteDone", false);
|
|
152
|
-
_define_property(this, "
|
|
176
|
+
_define_property(this, "reportStreamId", void 0);
|
|
177
|
+
_define_property(this, "writtenScreenshots", new Set());
|
|
153
178
|
_define_property(this, "initialized", false);
|
|
179
|
+
_define_property(this, "lastExecution", void 0);
|
|
180
|
+
_define_property(this, "lastGroupMeta", void 0);
|
|
154
181
|
_define_property(this, "writeQueue", Promise.resolve());
|
|
155
182
|
_define_property(this, "destroyed", false);
|
|
156
183
|
this.reportPath = options.reportPath;
|
|
157
184
|
this.screenshotMode = options.screenshotMode;
|
|
158
185
|
this.autoPrint = options.autoPrint ?? true;
|
|
186
|
+
this.reportStreamId = (0, utils_namespaceObject.uuid)();
|
|
159
187
|
this.printReportPath('will be generated at');
|
|
160
188
|
}
|
|
161
189
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report-generator.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/report-generator.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n existsSync,\n mkdirSync,\n statSync,\n truncateSync,\n writeFileSync,\n} from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport {\n MIDSCENE_REPORT_QUIET,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { ifInBrowser, logMsg } from '@midscene/shared/utils';\nimport {\n generateDumpScriptTag,\n generateImageScriptTag,\n getBaseUrlFixScript,\n} from './dump/html-utils';\nimport type { GroupedActionDump } from './types';\nimport { appendFileSync, getReportTpl } from './utils';\n\nexport interface IReportGenerator {\n /**\n * Schedule a dump update. Writes are queued internally to guarantee serial execution.\n * This method returns immediately (fire-and-forget).\n * Screenshots are written and memory is released during this call.\n */\n onDumpUpdate(dump: GroupedActionDump): void;\n /**\n * Wait for all queued write operations to complete.\n */\n flush(): Promise<void>;\n /**\n * Finalize the report. Calls flush() internally.\n */\n finalize(dump: GroupedActionDump): Promise<string | undefined>;\n getReportPath(): string | undefined;\n}\n\nexport const nullReportGenerator: IReportGenerator = {\n onDumpUpdate: () => {},\n flush: async () => {},\n finalize: async () => undefined,\n getReportPath: () => undefined,\n};\n\nexport class ReportGenerator implements IReportGenerator {\n private reportPath: string;\n private screenshotMode: 'inline' | 'directory';\n private autoPrint: boolean;\n private writtenScreenshots = new Set<string>();\n private firstWriteDone = false;\n\n // inline mode state\n private imageEndOffset = 0;\n private initialized = false;\n\n // write queue for serial execution\n private writeQueue: Promise<void> = Promise.resolve();\n private destroyed = false;\n\n constructor(options: {\n reportPath: string;\n screenshotMode: 'inline' | 'directory';\n autoPrint?: boolean;\n }) {\n this.reportPath = options.reportPath;\n this.screenshotMode = options.screenshotMode;\n this.autoPrint = options.autoPrint ?? true;\n this.printReportPath('will be generated at');\n }\n\n static create(\n reportFileName: string,\n opts: {\n generateReport?: boolean;\n outputFormat?: 'single-html' | 'html-and-external-assets';\n autoPrintReportMsg?: boolean;\n },\n ): IReportGenerator {\n if (opts.generateReport === false) return nullReportGenerator;\n\n // In browser environment, file system is not available\n if (ifInBrowser) return nullReportGenerator;\n\n if (opts.outputFormat === 'html-and-external-assets') {\n const outputDir = join(getMidsceneRunSubDir('report'), reportFileName);\n return new ReportGenerator({\n reportPath: join(outputDir, 'index.html'),\n screenshotMode: 'directory',\n autoPrint: opts.autoPrintReportMsg,\n });\n }\n\n return new ReportGenerator({\n reportPath: join(\n getMidsceneRunSubDir('report'),\n `${reportFileName}.html`,\n ),\n screenshotMode: 'inline',\n autoPrint: opts.autoPrintReportMsg,\n });\n }\n\n onDumpUpdate(dump: GroupedActionDump): void {\n this.writeQueue = this.writeQueue.then(() => {\n if (this.destroyed) return;\n this.doWrite(dump);\n });\n }\n\n async flush(): Promise<void> {\n await this.writeQueue;\n }\n\n async finalize(dump: GroupedActionDump): Promise<string | undefined> {\n this.onDumpUpdate(dump);\n await this.flush();\n this.destroyed = true;\n this.printReportPath('finalized');\n\n return this.reportPath;\n }\n\n getReportPath(): string | undefined {\n return this.reportPath;\n }\n\n private printReportPath(verb: string): void {\n if (!this.autoPrint || !this.reportPath) return;\n if (globalConfigManager.getEnvConfigInBoolean(MIDSCENE_REPORT_QUIET))\n return;\n\n if (this.screenshotMode === 'directory') {\n logMsg(\n `Midscene - report ${verb}: npx serve ${dirname(this.reportPath)}`,\n );\n } else {\n logMsg(`Midscene - report ${verb}: ${this.reportPath}`);\n }\n }\n\n private doWrite(dump: GroupedActionDump): void {\n if (this.screenshotMode === 'inline') {\n this.writeInlineReport(dump);\n } else {\n this.writeDirectoryReport(dump);\n }\n if (!this.firstWriteDone) {\n this.firstWriteDone = true;\n this.printReportPath('generated');\n }\n }\n\n private writeInlineReport(dump: GroupedActionDump): void {\n const dir = dirname(this.reportPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n if (!this.initialized) {\n writeFileSync(this.reportPath, getReportTpl());\n this.imageEndOffset = statSync(this.reportPath).size;\n this.initialized = true;\n }\n\n // 1. truncate: remove old dump JSON, keep template + existing image tags\n truncateSync(this.reportPath, this.imageEndOffset);\n\n // 2. append new image tags and release memory immediately after writing\n // Screenshots can be recovered from HTML file via lazy loading\n const screenshots = dump.collectAllScreenshots();\n for (const screenshot of screenshots) {\n if (!this.writtenScreenshots.has(screenshot.id)) {\n appendFileSync(\n this.reportPath,\n `\\n${generateImageScriptTag(screenshot.id, screenshot.base64)}`,\n );\n this.writtenScreenshots.add(screenshot.id);\n // Release memory - screenshot can be recovered via extractImageByIdSync\n screenshot.markPersistedInline(this.reportPath);\n }\n }\n\n // 3. update image end offset\n this.imageEndOffset = statSync(this.reportPath).size;\n\n // 4. append new dump JSON (compact { $screenshot: id } format)\n const serialized = dump.serialize();\n appendFileSync(this.reportPath, `\\n${generateDumpScriptTag(serialized)}`);\n }\n\n private writeDirectoryReport(dump: GroupedActionDump): void {\n const dir = dirname(this.reportPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // create screenshots subdirectory\n const screenshotsDir = join(dir, 'screenshots');\n if (!existsSync(screenshotsDir)) {\n mkdirSync(screenshotsDir, { recursive: true });\n }\n\n // 1. write new screenshots and release memory immediately\n // Screenshots can be recovered from disk via lazy loading\n const screenshots = dump.collectAllScreenshots();\n for (const screenshot of screenshots) {\n if (!this.writtenScreenshots.has(screenshot.id)) {\n const ext = screenshot.extension;\n const absolutePath = join(screenshotsDir, `${screenshot.id}.${ext}`);\n const buffer = Buffer.from(screenshot.rawBase64, 'base64');\n writeFileSync(absolutePath, buffer);\n this.writtenScreenshots.add(screenshot.id);\n screenshot.markPersistedToPath(\n `./screenshots/${screenshot.id}.${ext}`,\n absolutePath,\n );\n }\n }\n\n // 2. write HTML with dump JSON (toSerializable() returns { $screenshot: id } format)\n const serialized = dump.serialize();\n writeFileSync(\n this.reportPath,\n `${getReportTpl()}${getBaseUrlFixScript()}${generateDumpScriptTag(serialized)}`,\n );\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","nullReportGenerator","undefined","ReportGenerator","reportFileName","opts","ifInBrowser","outputDir","join","getMidsceneRunSubDir","dump","verb","globalConfigManager","MIDSCENE_REPORT_QUIET","logMsg","dirname","dir","existsSync","mkdirSync","writeFileSync","getReportTpl","statSync","truncateSync","screenshots","screenshot","appendFileSync","generateImageScriptTag","serialized","generateDumpScriptTag","screenshotsDir","ext","absolutePath","buffer","Buffer","getBaseUrlFixScript","options","Set","Promise"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"report-generator.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/report-generator.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport {\n MIDSCENE_REPORT_QUIET,\n globalConfigManager,\n} from '@midscene/shared/env';\nimport { ifInBrowser, logMsg, uuid } from '@midscene/shared/utils';\nimport {\n generateDumpScriptTag,\n generateImageScriptTag,\n getBaseUrlFixScript,\n} from './dump/html-utils';\nimport { type ExecutionDump, type GroupMeta, GroupedActionDump } from './types';\nimport { appendFileSync, getReportTpl } from './utils';\n\nexport interface IReportGenerator {\n /**\n * Write or update a single execution.\n * Each call appends a new dump script tag. The frontend deduplicates\n * executions with the same id/name, keeping only the last one.\n *\n * @param execution Current execution's full data\n * @param groupMeta Group-level metadata (groupName, sdkVersion, etc.)\n */\n onExecutionUpdate(execution: ExecutionDump, groupMeta: GroupMeta): void;\n\n /**\n * @deprecated Use onExecutionUpdate instead. Kept for backward compatibility.\n */\n onDumpUpdate?(dump: GroupedActionDump): void;\n\n /**\n * Wait for all queued write operations to complete.\n */\n flush(): Promise<void>;\n\n /**\n * Finalize the report. Calls flush() internally.\n */\n finalize(): Promise<string | undefined>;\n\n getReportPath(): string | undefined;\n}\n\nexport const nullReportGenerator: IReportGenerator = {\n onExecutionUpdate: () => {},\n flush: async () => {},\n finalize: async () => undefined,\n getReportPath: () => undefined,\n};\n\nexport class ReportGenerator implements IReportGenerator {\n private reportPath: string;\n private screenshotMode: 'inline' | 'directory';\n private autoPrint: boolean;\n private firstWriteDone = false;\n\n // Unique identifier for this report stream — used as data-group-id\n private readonly reportStreamId: string;\n\n // Tracks screenshots already written to disk (by id) to avoid duplicates\n private writtenScreenshots = new Set<string>();\n private initialized = false;\n\n // Tracks the last execution + groupMeta for re-writing on finalize\n private lastExecution?: ExecutionDump;\n private lastGroupMeta?: GroupMeta;\n\n // write queue for serial execution\n private writeQueue: Promise<void> = Promise.resolve();\n private destroyed = false;\n\n constructor(options: {\n reportPath: string;\n screenshotMode: 'inline' | 'directory';\n autoPrint?: boolean;\n }) {\n this.reportPath = options.reportPath;\n this.screenshotMode = options.screenshotMode;\n this.autoPrint = options.autoPrint ?? true;\n this.reportStreamId = uuid();\n this.printReportPath('will be generated at');\n }\n\n static create(\n reportFileName: string,\n opts: {\n generateReport?: boolean;\n outputFormat?: 'single-html' | 'html-and-external-assets';\n autoPrintReportMsg?: boolean;\n },\n ): IReportGenerator {\n if (opts.generateReport === false) return nullReportGenerator;\n\n // In browser environment, file system is not available\n if (ifInBrowser) return nullReportGenerator;\n\n if (opts.outputFormat === 'html-and-external-assets') {\n const outputDir = join(getMidsceneRunSubDir('report'), reportFileName);\n return new ReportGenerator({\n reportPath: join(outputDir, 'index.html'),\n screenshotMode: 'directory',\n autoPrint: opts.autoPrintReportMsg,\n });\n }\n\n return new ReportGenerator({\n reportPath: join(\n getMidsceneRunSubDir('report'),\n `${reportFileName}.html`,\n ),\n screenshotMode: 'inline',\n autoPrint: opts.autoPrintReportMsg,\n });\n }\n\n onExecutionUpdate(execution: ExecutionDump, groupMeta: GroupMeta): void {\n this.lastExecution = execution;\n this.lastGroupMeta = groupMeta;\n this.writeQueue = this.writeQueue.then(() => {\n if (this.destroyed) return;\n this.doWriteExecution(execution, groupMeta);\n });\n }\n\n async flush(): Promise<void> {\n await this.writeQueue;\n }\n\n async finalize(): Promise<string | undefined> {\n // Re-write the last execution to capture any final state changes\n if (this.lastExecution && this.lastGroupMeta) {\n this.onExecutionUpdate(this.lastExecution, this.lastGroupMeta);\n }\n await this.flush();\n this.destroyed = true;\n\n if (!this.initialized) {\n // No executions were ever written — no file exists\n return undefined;\n }\n\n this.printReportPath('finalized');\n return this.reportPath;\n }\n\n getReportPath(): string | undefined {\n return this.reportPath;\n }\n\n private printReportPath(verb: string): void {\n if (!this.autoPrint || !this.reportPath) return;\n if (globalConfigManager.getEnvConfigInBoolean(MIDSCENE_REPORT_QUIET))\n return;\n\n if (this.screenshotMode === 'directory') {\n logMsg(\n `Midscene - report ${verb}: npx serve ${dirname(this.reportPath)}`,\n );\n } else {\n logMsg(`Midscene - report ${verb}: ${this.reportPath}`);\n }\n }\n\n private doWriteExecution(\n execution: ExecutionDump,\n groupMeta: GroupMeta,\n ): void {\n if (this.screenshotMode === 'inline') {\n this.writeInlineExecution(execution, groupMeta);\n } else {\n this.writeDirectoryExecution(execution, groupMeta);\n }\n if (!this.firstWriteDone) {\n this.firstWriteDone = true;\n this.printReportPath('generated');\n }\n }\n\n /**\n * Wrap an ExecutionDump + GroupMeta into a single-execution GroupedActionDump.\n */\n private wrapAsGroupedDump(\n execution: ExecutionDump,\n groupMeta: GroupMeta,\n ): GroupedActionDump {\n return new GroupedActionDump({\n sdkVersion: groupMeta.sdkVersion,\n groupName: groupMeta.groupName,\n groupDescription: groupMeta.groupDescription,\n modelBriefs: groupMeta.modelBriefs,\n deviceType: groupMeta.deviceType,\n executions: [execution],\n });\n }\n\n /**\n * Append-only inline mode: write new screenshots and a dump tag on every call.\n * The frontend deduplicates executions with the same id/name (keeps last).\n * Duplicate dump JSON is acceptable; only screenshots are deduplicated.\n */\n private writeInlineExecution(\n execution: ExecutionDump,\n groupMeta: GroupMeta,\n ): void {\n const dir = dirname(this.reportPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // Initialize: write HTML template once\n if (!this.initialized) {\n writeFileSync(this.reportPath, getReportTpl());\n this.initialized = true;\n }\n\n // Append new screenshots (skip already-written ones)\n const screenshots = execution.collectScreenshots();\n for (const screenshot of screenshots) {\n if (!this.writtenScreenshots.has(screenshot.id)) {\n appendFileSync(\n this.reportPath,\n `\\n${generateImageScriptTag(screenshot.id, screenshot.base64)}`,\n );\n this.writtenScreenshots.add(screenshot.id);\n // Safe to release memory — the image tag is permanent (never truncated)\n screenshot.markPersistedInline(this.reportPath);\n }\n }\n\n // Append dump tag (always — frontend keeps only last per execution id)\n const singleDump = this.wrapAsGroupedDump(execution, groupMeta);\n const serialized = singleDump.serialize();\n const attributes: Record<string, string> = {\n 'data-group-id': this.reportStreamId,\n };\n appendFileSync(\n this.reportPath,\n `\\n${generateDumpScriptTag(serialized, attributes)}`,\n );\n }\n\n private writeDirectoryExecution(\n execution: ExecutionDump,\n groupMeta: GroupMeta,\n ): void {\n const dir = dirname(this.reportPath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n // create screenshots subdirectory\n const screenshotsDir = join(dir, 'screenshots');\n if (!existsSync(screenshotsDir)) {\n mkdirSync(screenshotsDir, { recursive: true });\n }\n\n // 1. Write new screenshots and release memory immediately\n const screenshots = execution.collectScreenshots();\n for (const screenshot of screenshots) {\n if (!this.writtenScreenshots.has(screenshot.id)) {\n const ext = screenshot.extension;\n const absolutePath = join(screenshotsDir, `${screenshot.id}.${ext}`);\n const buffer = Buffer.from(screenshot.rawBase64, 'base64');\n writeFileSync(absolutePath, buffer);\n this.writtenScreenshots.add(screenshot.id);\n screenshot.markPersistedToPath(\n `./screenshots/${screenshot.id}.${ext}`,\n absolutePath,\n );\n }\n }\n\n // 2. Append dump tag (always — frontend keeps only last per execution id)\n const singleDump = this.wrapAsGroupedDump(execution, groupMeta);\n const serialized = singleDump.serialize();\n const dumpAttributes: Record<string, string> = {\n 'data-group-id': this.reportStreamId,\n };\n\n if (!this.initialized) {\n writeFileSync(\n this.reportPath,\n `${getReportTpl()}${getBaseUrlFixScript()}`,\n );\n this.initialized = true;\n }\n\n appendFileSync(\n this.reportPath,\n `\\n${generateDumpScriptTag(serialized, dumpAttributes)}`,\n );\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","nullReportGenerator","undefined","ReportGenerator","reportFileName","opts","ifInBrowser","outputDir","join","getMidsceneRunSubDir","execution","groupMeta","verb","globalConfigManager","MIDSCENE_REPORT_QUIET","logMsg","dirname","GroupedActionDump","dir","existsSync","mkdirSync","writeFileSync","getReportTpl","screenshots","screenshot","appendFileSync","generateImageScriptTag","singleDump","serialized","attributes","generateDumpScriptTag","screenshotsDir","ext","absolutePath","buffer","Buffer","dumpAttributes","getBaseUrlFixScript","options","Set","Promise","uuid"],"mappings":"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"}
|
package/dist/lib/report.js
CHANGED
|
@@ -32,6 +32,7 @@ const common_namespaceObject = require("@midscene/shared/common");
|
|
|
32
32
|
const utils_namespaceObject = require("@midscene/shared/utils");
|
|
33
33
|
const index_js_namespaceObject = require("./agent/index.js");
|
|
34
34
|
const html_utils_js_namespaceObject = require("./dump/html-utils.js");
|
|
35
|
+
const external_types_js_namespaceObject = require("./types.js");
|
|
35
36
|
const external_utils_js_namespaceObject = require("./utils.js");
|
|
36
37
|
function _define_property(obj, key, value) {
|
|
37
38
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -54,6 +55,27 @@ class ReportMergingTool {
|
|
|
54
55
|
const reportDir = external_node_path_namespaceObject.dirname(reportFilePath);
|
|
55
56
|
return 'index.html' === external_node_path_namespaceObject.basename(reportFilePath) && (0, external_node_fs_namespaceObject.existsSync)(external_node_path_namespaceObject.join(reportDir, 'screenshots'));
|
|
56
57
|
}
|
|
58
|
+
mergeDumpScripts(contents) {
|
|
59
|
+
const unescaped = contents.map((c)=>(0, utils_namespaceObject.antiEscapeScriptTag)(c)).filter((c)=>c.length > 0);
|
|
60
|
+
if (0 === unescaped.length) return '';
|
|
61
|
+
if (1 === unescaped.length) return unescaped[0];
|
|
62
|
+
const base = external_types_js_namespaceObject.GroupedActionDump.fromSerializedString(unescaped[0]);
|
|
63
|
+
const allExecutions = [
|
|
64
|
+
...base.executions
|
|
65
|
+
];
|
|
66
|
+
for(let i = 1; i < unescaped.length; i++){
|
|
67
|
+
const other = external_types_js_namespaceObject.GroupedActionDump.fromSerializedString(unescaped[i]);
|
|
68
|
+
allExecutions.push(...other.executions);
|
|
69
|
+
}
|
|
70
|
+
let noIdCounter = 0;
|
|
71
|
+
const deduped = new Map();
|
|
72
|
+
for (const exec of allExecutions){
|
|
73
|
+
const key = exec.id || `__no_id_${noIdCounter++}`;
|
|
74
|
+
deduped.set(key, exec);
|
|
75
|
+
}
|
|
76
|
+
base.executions = Array.from(deduped.values());
|
|
77
|
+
return base.serialize();
|
|
78
|
+
}
|
|
57
79
|
mergeReports(reportFileName = 'AUTO', opts) {
|
|
58
80
|
if (this.reportInfos.length <= 1) {
|
|
59
81
|
(0, utils_namespaceObject.logMsg)('Not enough reports to merge');
|
|
@@ -95,11 +117,15 @@ class ReportMergingTool {
|
|
|
95
117
|
(0, external_node_fs_namespaceObject.copyFileSync)(src, dest);
|
|
96
118
|
}
|
|
97
119
|
} else (0, html_utils_js_namespaceObject.streamImageScriptsToFile)(reportInfo.reportFilePath, outputFilePath);
|
|
98
|
-
const
|
|
120
|
+
const allDumps = (0, html_utils_js_namespaceObject.extractAllDumpScriptsSync)(reportInfo.reportFilePath).filter((d)=>d.openTag.includes('data-group-id'));
|
|
121
|
+
const groupIdMatch = allDumps[0]?.openTag.match(/data-group-id="([^"]+)"/);
|
|
122
|
+
const mergedGroupId = groupIdMatch ? decodeURIComponent(groupIdMatch[1]) : `merged-group-${i}`;
|
|
123
|
+
const dumpString = allDumps.length > 0 ? this.mergeDumpScripts(allDumps.map((d)=>d.content)) : (0, html_utils_js_namespaceObject.extractLastDumpScriptSync)(reportInfo.reportFilePath);
|
|
99
124
|
const { reportAttributes } = reportInfo;
|
|
100
125
|
const reportHtmlStr = `${(0, external_utils_js_namespaceObject.reportHTMLContent)({
|
|
101
126
|
dumpString,
|
|
102
127
|
attributes: {
|
|
128
|
+
'data-group-id': mergedGroupId,
|
|
103
129
|
playwright_test_duration: reportAttributes.testDuration,
|
|
104
130
|
playwright_test_status: reportAttributes.testStatus,
|
|
105
131
|
playwright_test_title: reportAttributes.testTitle,
|
package/dist/lib/report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/report.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n appendFileSync,\n copyFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n rmSync,\n unlinkSync,\n} from 'node:fs';\nimport * as path from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { logMsg } from '@midscene/shared/utils';\nimport { getReportFileName } from './agent';\nimport {\n extractLastDumpScriptSync,\n getBaseUrlFixScript,\n streamImageScriptsToFile,\n} from './dump/html-utils';\nimport type { ReportFileWithAttributes } from './types';\nimport { getReportTpl, reportHTMLContent } from './utils';\n\nexport class ReportMergingTool {\n private reportInfos: ReportFileWithAttributes[] = [];\n public append(reportInfo: ReportFileWithAttributes) {\n this.reportInfos.push(reportInfo);\n }\n public clear() {\n this.reportInfos = [];\n }\n\n /**\n * Check if a report is in directory mode (html-and-external-assets).\n * Directory mode reports: {name}/index.html + {name}/screenshots/\n */\n private isDirectoryModeReport(reportFilePath: string): boolean {\n const reportDir = path.dirname(reportFilePath);\n return (\n path.basename(reportFilePath) === 'index.html' &&\n existsSync(path.join(reportDir, 'screenshots'))\n );\n }\n\n public mergeReports(\n reportFileName: 'AUTO' | string = 'AUTO',\n opts?: {\n rmOriginalReports?: boolean;\n overwrite?: boolean;\n },\n ): string | null {\n if (this.reportInfos.length <= 1) {\n logMsg('Not enough reports to merge');\n return null;\n }\n\n const { rmOriginalReports = false, overwrite = false } = opts ?? {};\n const targetDir = getMidsceneRunSubDir('report');\n\n // Check if any source report is directory mode\n const hasDirectoryModeReport = this.reportInfos.some((info) =>\n this.isDirectoryModeReport(info.reportFilePath),\n );\n\n const resolvedName =\n reportFileName === 'AUTO'\n ? getReportFileName('merged-report')\n : reportFileName;\n\n // Directory mode: output as {name}/index.html to keep relative paths working\n // Inline mode: output as {name}.html (single file)\n const outputFilePath = hasDirectoryModeReport\n ? path.resolve(targetDir, resolvedName, 'index.html')\n : path.resolve(targetDir, `${resolvedName}.html`);\n\n if (reportFileName !== 'AUTO' && existsSync(outputFilePath)) {\n if (!overwrite) {\n throw new Error(\n `Report file already exists: ${outputFilePath}\\nSet overwrite to true to overwrite this file.`,\n );\n }\n if (hasDirectoryModeReport) {\n rmSync(path.dirname(outputFilePath), { recursive: true, force: true });\n } else {\n unlinkSync(outputFilePath);\n }\n }\n\n if (hasDirectoryModeReport) {\n mkdirSync(path.dirname(outputFilePath), { recursive: true });\n }\n\n logMsg(\n `Start merging ${this.reportInfos.length} reports...\\nCreating template file...`,\n );\n\n try {\n // Write template\n appendFileSync(outputFilePath, getReportTpl());\n\n // For directory-mode output, inject base URL fix script\n if (hasDirectoryModeReport) {\n appendFileSync(outputFilePath, getBaseUrlFixScript());\n }\n\n // Process all reports one by one\n for (let i = 0; i < this.reportInfos.length; i++) {\n const reportInfo = this.reportInfos[i];\n logMsg(`Processing report ${i + 1}/${this.reportInfos.length}`);\n\n if (this.isDirectoryModeReport(reportInfo.reportFilePath)) {\n // Directory mode: copy external screenshot files\n const reportDir = path.dirname(reportInfo.reportFilePath);\n const screenshotsDir = path.join(reportDir, 'screenshots');\n const mergedScreenshotsDir = path.join(\n path.dirname(outputFilePath),\n 'screenshots',\n );\n mkdirSync(mergedScreenshotsDir, { recursive: true });\n for (const file of readdirSync(screenshotsDir)) {\n const src = path.join(screenshotsDir, file);\n const dest = path.join(mergedScreenshotsDir, file);\n copyFileSync(src, dest);\n }\n } else {\n // Inline mode: stream image scripts to output file\n streamImageScriptsToFile(reportInfo.reportFilePath, outputFilePath);\n }\n\n const dumpString = extractLastDumpScriptSync(reportInfo.reportFilePath);\n const { reportAttributes } = reportInfo;\n\n const reportHtmlStr = `${reportHTMLContent(\n {\n dumpString,\n attributes: {\n playwright_test_duration: reportAttributes.testDuration,\n playwright_test_status: reportAttributes.testStatus,\n playwright_test_title: reportAttributes.testTitle,\n playwright_test_id: reportAttributes.testId,\n playwright_test_description: reportAttributes.testDescription,\n },\n },\n undefined,\n undefined,\n false,\n )}\\n`;\n\n appendFileSync(outputFilePath, reportHtmlStr);\n }\n\n logMsg(`Successfully merged new report: ${outputFilePath}`);\n\n // Remove original reports if needed\n if (rmOriginalReports) {\n for (const info of this.reportInfos) {\n try {\n if (this.isDirectoryModeReport(info.reportFilePath)) {\n // Directory mode: remove the entire report directory\n const reportDir = path.dirname(info.reportFilePath);\n rmSync(reportDir, { recursive: true, force: true });\n } else {\n unlinkSync(info.reportFilePath);\n }\n } catch (error) {\n logMsg(`Error deleting report ${info.reportFilePath}: ${error}`);\n }\n }\n logMsg(`Removed ${this.reportInfos.length} original reports`);\n }\n return outputFilePath;\n } catch (error) {\n logMsg(`Error in mergeReports: ${error}`);\n throw error;\n }\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","ReportMergingTool","reportInfo","reportFilePath","reportDir","path","existsSync","reportFileName","opts","logMsg","rmOriginalReports","overwrite","targetDir","getMidsceneRunSubDir","hasDirectoryModeReport","info","resolvedName","getReportFileName","outputFilePath","Error","rmSync","unlinkSync","mkdirSync","appendFileSync","getReportTpl","getBaseUrlFixScript","i","screenshotsDir","mergedScreenshotsDir","file","readdirSync","src","dest","copyFileSync","streamImageScriptsToFile","dumpString","extractLastDumpScriptSync","reportAttributes","reportHtmlStr","reportHTMLContent","undefined","error"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"report.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/report.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n appendFileSync,\n copyFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n rmSync,\n unlinkSync,\n} from 'node:fs';\nimport * as path from 'node:path';\nimport { getMidsceneRunSubDir } from '@midscene/shared/common';\nimport { antiEscapeScriptTag, logMsg } from '@midscene/shared/utils';\nimport { getReportFileName } from './agent';\nimport {\n extractAllDumpScriptsSync,\n extractLastDumpScriptSync,\n getBaseUrlFixScript,\n streamImageScriptsToFile,\n} from './dump/html-utils';\nimport { GroupedActionDump } from './types';\nimport type { ReportFileWithAttributes } from './types';\nimport { getReportTpl, reportHTMLContent } from './utils';\n\nexport class ReportMergingTool {\n private reportInfos: ReportFileWithAttributes[] = [];\n public append(reportInfo: ReportFileWithAttributes) {\n this.reportInfos.push(reportInfo);\n }\n public clear() {\n this.reportInfos = [];\n }\n\n /**\n * Check if a report is in directory mode (html-and-external-assets).\n * Directory mode reports: {name}/index.html + {name}/screenshots/\n */\n private isDirectoryModeReport(reportFilePath: string): boolean {\n const reportDir = path.dirname(reportFilePath);\n return (\n path.basename(reportFilePath) === 'index.html' &&\n existsSync(path.join(reportDir, 'screenshots'))\n );\n }\n\n /**\n * Merge multiple dump script contents (from the same source report)\n * into a single serialized GroupedActionDump string.\n * If there's only one dump, return it as-is. If multiple, merge\n * all executions into the first dump's group structure.\n */\n private mergeDumpScripts(contents: string[]): string {\n const unescaped = contents\n .map((c) => antiEscapeScriptTag(c))\n .filter((c) => c.length > 0);\n if (unescaped.length === 0) return '';\n if (unescaped.length === 1) return unescaped[0];\n\n // Parse all dumps and collect executions, deduplicating by id (keep last).\n // Only executions with a stable id are deduped; old-format entries without\n // id are always kept (they may be distinct despite sharing the same name).\n const base = GroupedActionDump.fromSerializedString(unescaped[0]);\n const allExecutions = [...base.executions];\n for (let i = 1; i < unescaped.length; i++) {\n const other = GroupedActionDump.fromSerializedString(unescaped[i]);\n allExecutions.push(...other.executions);\n }\n let noIdCounter = 0;\n const deduped = new Map<string, (typeof allExecutions)[0]>();\n for (const exec of allExecutions) {\n const key = exec.id || `__no_id_${noIdCounter++}`;\n deduped.set(key, exec);\n }\n base.executions = Array.from(deduped.values());\n return base.serialize();\n }\n\n public mergeReports(\n reportFileName: 'AUTO' | string = 'AUTO',\n opts?: {\n rmOriginalReports?: boolean;\n overwrite?: boolean;\n },\n ): string | null {\n if (this.reportInfos.length <= 1) {\n logMsg('Not enough reports to merge');\n return null;\n }\n\n const { rmOriginalReports = false, overwrite = false } = opts ?? {};\n const targetDir = getMidsceneRunSubDir('report');\n\n // Check if any source report is directory mode\n const hasDirectoryModeReport = this.reportInfos.some((info) =>\n this.isDirectoryModeReport(info.reportFilePath),\n );\n\n const resolvedName =\n reportFileName === 'AUTO'\n ? getReportFileName('merged-report')\n : reportFileName;\n\n // Directory mode: output as {name}/index.html to keep relative paths working\n // Inline mode: output as {name}.html (single file)\n const outputFilePath = hasDirectoryModeReport\n ? path.resolve(targetDir, resolvedName, 'index.html')\n : path.resolve(targetDir, `${resolvedName}.html`);\n\n if (reportFileName !== 'AUTO' && existsSync(outputFilePath)) {\n if (!overwrite) {\n throw new Error(\n `Report file already exists: ${outputFilePath}\\nSet overwrite to true to overwrite this file.`,\n );\n }\n if (hasDirectoryModeReport) {\n rmSync(path.dirname(outputFilePath), { recursive: true, force: true });\n } else {\n unlinkSync(outputFilePath);\n }\n }\n\n if (hasDirectoryModeReport) {\n mkdirSync(path.dirname(outputFilePath), { recursive: true });\n }\n\n logMsg(\n `Start merging ${this.reportInfos.length} reports...\\nCreating template file...`,\n );\n\n try {\n // Write template\n appendFileSync(outputFilePath, getReportTpl());\n\n // For directory-mode output, inject base URL fix script\n if (hasDirectoryModeReport) {\n appendFileSync(outputFilePath, getBaseUrlFixScript());\n }\n\n // Process all reports one by one\n for (let i = 0; i < this.reportInfos.length; i++) {\n const reportInfo = this.reportInfos[i];\n logMsg(`Processing report ${i + 1}/${this.reportInfos.length}`);\n\n if (this.isDirectoryModeReport(reportInfo.reportFilePath)) {\n // Directory mode: copy external screenshot files\n const reportDir = path.dirname(reportInfo.reportFilePath);\n const screenshotsDir = path.join(reportDir, 'screenshots');\n const mergedScreenshotsDir = path.join(\n path.dirname(outputFilePath),\n 'screenshots',\n );\n mkdirSync(mergedScreenshotsDir, { recursive: true });\n for (const file of readdirSync(screenshotsDir)) {\n const src = path.join(screenshotsDir, file);\n const dest = path.join(mergedScreenshotsDir, file);\n copyFileSync(src, dest);\n }\n } else {\n // Inline mode: stream image scripts to output file\n streamImageScriptsToFile(reportInfo.reportFilePath, outputFilePath);\n }\n\n // Extract all dump scripts from the source report.\n // After the per-execution append refactor, a single source report\n // may contain multiple <script type=\"midscene_web_dump\"> tags\n // (one per execution). We merge them into a single GroupedActionDump.\n // Filter by data-group-id to exclude false matches from the template's\n // bundled JS code, which also references the midscene_web_dump type string.\n const allDumps = extractAllDumpScriptsSync(\n reportInfo.reportFilePath,\n ).filter((d) => d.openTag.includes('data-group-id'));\n const groupIdMatch = allDumps[0]?.openTag.match(\n /data-group-id=\"([^\"]+)\"/,\n );\n const mergedGroupId = groupIdMatch\n ? decodeURIComponent(groupIdMatch[1])\n : `merged-group-${i}`;\n const dumpString =\n allDumps.length > 0\n ? this.mergeDumpScripts(allDumps.map((d) => d.content))\n : extractLastDumpScriptSync(reportInfo.reportFilePath);\n const { reportAttributes } = reportInfo;\n\n const reportHtmlStr = `${reportHTMLContent(\n {\n dumpString,\n attributes: {\n 'data-group-id': mergedGroupId,\n playwright_test_duration: reportAttributes.testDuration,\n playwright_test_status: reportAttributes.testStatus,\n playwright_test_title: reportAttributes.testTitle,\n playwright_test_id: reportAttributes.testId,\n playwright_test_description: reportAttributes.testDescription,\n },\n },\n undefined,\n undefined,\n false,\n )}\\n`;\n\n appendFileSync(outputFilePath, reportHtmlStr);\n }\n\n logMsg(`Successfully merged new report: ${outputFilePath}`);\n\n // Remove original reports if needed\n if (rmOriginalReports) {\n for (const info of this.reportInfos) {\n try {\n if (this.isDirectoryModeReport(info.reportFilePath)) {\n // Directory mode: remove the entire report directory\n const reportDir = path.dirname(info.reportFilePath);\n rmSync(reportDir, { recursive: true, force: true });\n } else {\n unlinkSync(info.reportFilePath);\n }\n } catch (error) {\n logMsg(`Error deleting report ${info.reportFilePath}: ${error}`);\n }\n }\n logMsg(`Removed ${this.reportInfos.length} original reports`);\n }\n return outputFilePath;\n } catch (error) {\n logMsg(`Error in mergeReports: ${error}`);\n throw error;\n }\n }\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","ReportMergingTool","reportInfo","reportFilePath","reportDir","path","existsSync","contents","unescaped","c","antiEscapeScriptTag","base","GroupedActionDump","allExecutions","i","other","noIdCounter","deduped","Map","exec","Array","reportFileName","opts","logMsg","rmOriginalReports","overwrite","targetDir","getMidsceneRunSubDir","hasDirectoryModeReport","info","resolvedName","getReportFileName","outputFilePath","Error","rmSync","unlinkSync","mkdirSync","appendFileSync","getReportTpl","getBaseUrlFixScript","screenshotsDir","mergedScreenshotsDir","file","readdirSync","src","dest","copyFileSync","streamImageScriptsToFile","allDumps","extractAllDumpScriptsSync","d","groupIdMatch","mergedGroupId","decodeURIComponent","dumpString","extractLastDumpScriptSync","reportAttributes","reportHtmlStr","reportHTMLContent","undefined","error"],"mappings":"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"}
|
package/dist/lib/task-runner.js
CHANGED
|
@@ -236,6 +236,7 @@ class TaskRunner {
|
|
|
236
236
|
}
|
|
237
237
|
dump() {
|
|
238
238
|
return new external_types_js_namespaceObject.ExecutionDump({
|
|
239
|
+
id: this.id,
|
|
239
240
|
logTime: this.executionLogTime,
|
|
240
241
|
name: this.name,
|
|
241
242
|
tasks: this.tasks
|
|
@@ -260,6 +261,7 @@ class TaskRunner {
|
|
|
260
261
|
};
|
|
261
262
|
}
|
|
262
263
|
constructor(name, uiContextBuilder, options){
|
|
264
|
+
_define_property(this, "id", void 0);
|
|
263
265
|
_define_property(this, "name", void 0);
|
|
264
266
|
_define_property(this, "tasks", void 0);
|
|
265
267
|
_define_property(this, "status", void 0);
|
|
@@ -268,6 +270,7 @@ class TaskRunner {
|
|
|
268
270
|
_define_property(this, "onTaskUpdate", void 0);
|
|
269
271
|
_define_property(this, "executionLogTime", void 0);
|
|
270
272
|
_define_property(this, "lastUiContext", void 0);
|
|
273
|
+
this.id = (0, utils_namespaceObject.uuid)();
|
|
271
274
|
this.status = options?.tasks && options.tasks.length > 0 ? 'pending' : 'init';
|
|
272
275
|
this.name = name;
|
|
273
276
|
this.tasks = (options?.tasks || []).map((item)=>this.markTaskAsPending(item));
|