@midscene/web 1.0.1-beta-20251028065320.0 → 1.0.1-beta-20251028121806.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/bridge-mode/io-client.mjs +1 -1
- package/dist/es/bridge-mode/io-server.mjs +2 -2
- package/dist/es/bridge-mode/page-browser-side.mjs +1 -1
- package/dist/es/playwright/ai-fixture.mjs +6 -45
- package/dist/es/playwright/ai-fixture.mjs.map +1 -1
- package/dist/es/playwright/reporter/index.mjs +13 -25
- package/dist/es/playwright/reporter/index.mjs.map +1 -1
- package/dist/lib/bridge-mode/io-client.js +1 -1
- package/dist/lib/bridge-mode/io-server.js +2 -2
- package/dist/lib/bridge-mode/page-browser-side.js +1 -1
- package/dist/lib/playwright/ai-fixture.js +6 -45
- package/dist/lib/playwright/ai-fixture.js.map +1 -1
- package/dist/lib/playwright/reporter/index.js +12 -24
- package/dist/lib/playwright/reporter/index.js.map +1 -1
- package/dist/types/playwright/reporter/index.d.ts +3 -3
- package/package.json +4 -4
|
@@ -75,7 +75,7 @@ class BridgeServer {
|
|
|
75
75
|
logMsg('one client connected');
|
|
76
76
|
this.socket = socket;
|
|
77
77
|
const clientVersion = socket.handshake.query.version;
|
|
78
|
-
logMsg(`Bridge connected, cli-side version v1.0.1-beta-
|
|
78
|
+
logMsg(`Bridge connected, cli-side version v1.0.1-beta-20251028121806.0, browser-side version v${clientVersion}`);
|
|
79
79
|
socket.on(BridgeEvent.CallResponse, (params)=>{
|
|
80
80
|
const id = params.id;
|
|
81
81
|
const response = params.response;
|
|
@@ -103,7 +103,7 @@ class BridgeServer {
|
|
|
103
103
|
var _this_onConnect, _this;
|
|
104
104
|
null == (_this_onConnect = (_this = this).onConnect) || _this_onConnect.call(_this);
|
|
105
105
|
const payload = {
|
|
106
|
-
version: "1.0.1-beta-
|
|
106
|
+
version: "1.0.1-beta-20251028121806.0"
|
|
107
107
|
};
|
|
108
108
|
socket.emit(BridgeEvent.Connected, payload);
|
|
109
109
|
Promise.resolve().then(()=>{
|
|
@@ -46,7 +46,7 @@ class ExtensionBridgePageBrowserSide extends page {
|
|
|
46
46
|
}
|
|
47
47
|
}, ()=>this.destroy());
|
|
48
48
|
await this.bridgeClient.connect();
|
|
49
|
-
this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.0.1-beta-
|
|
49
|
+
this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.0.1-beta-20251028121806.0`, 'log');
|
|
50
50
|
}
|
|
51
51
|
async connect() {
|
|
52
52
|
return await this.setupBridgeClient();
|
|
@@ -33,48 +33,13 @@ const groupAndCaseForTest = (testInfo)=>{
|
|
|
33
33
|
};
|
|
34
34
|
const midsceneAgentKeyId = '_midsceneAgentId';
|
|
35
35
|
const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';
|
|
36
|
-
const
|
|
37
|
-
let cleanupHandlersRegistered = false;
|
|
38
|
-
let cleanupComplete = false;
|
|
39
|
-
function registerCleanupHandlers() {
|
|
40
|
-
if (cleanupHandlersRegistered) return;
|
|
41
|
-
cleanupHandlersRegistered = true;
|
|
42
|
-
const cleanup = ()=>{
|
|
43
|
-
if (cleanupComplete) return;
|
|
44
|
-
cleanupComplete = true;
|
|
45
|
-
debugPage(`Cleaning up ${globalTempFiles.size} temporary dump files`);
|
|
46
|
-
const filesToClean = Array.from(globalTempFiles);
|
|
47
|
-
for (const filePath of filesToClean)try {
|
|
48
|
-
rmSync(filePath, {
|
|
49
|
-
force: true
|
|
50
|
-
});
|
|
51
|
-
} catch (error) {
|
|
52
|
-
debugPage(`Failed to clean up temp file: ${filePath}`, error);
|
|
53
|
-
}
|
|
54
|
-
globalTempFiles.clear();
|
|
55
|
-
};
|
|
56
|
-
process.once('SIGINT', cleanup);
|
|
57
|
-
process.once('SIGTERM', cleanup);
|
|
58
|
-
process.once('SIGHUP', cleanup);
|
|
59
|
-
process.once('exit', cleanup);
|
|
60
|
-
process.once('beforeExit', cleanup);
|
|
61
|
-
process.once('uncaughtException', (error)=>{
|
|
62
|
-
debugPage('Uncaught exception detected, cleaning up temp files', error);
|
|
63
|
-
cleanup();
|
|
64
|
-
});
|
|
65
|
-
process.once('unhandledRejection', (reason)=>{
|
|
66
|
-
debugPage('Unhandled rejection detected, cleaning up temp files', reason);
|
|
67
|
-
cleanup();
|
|
68
|
-
});
|
|
69
|
-
}
|
|
36
|
+
const pageTempFiles = new Map();
|
|
70
37
|
const PlaywrightAiFixture = (options)=>{
|
|
71
38
|
const { forceSameTabNavigation = true, waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT, waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, cache } = options ?? {};
|
|
72
39
|
const processTestCacheConfig = (testInfo)=>{
|
|
73
40
|
const { id } = groupAndCaseForTest(testInfo);
|
|
74
41
|
return processCacheConfig(cache, id);
|
|
75
42
|
};
|
|
76
|
-
const pageTempFiles = new Map();
|
|
77
|
-
registerCleanupHandlers();
|
|
78
43
|
const pageAgentMap = {};
|
|
79
44
|
const createOrReuseAgentForPage = (page, testInfo, opts)=>{
|
|
80
45
|
let idForPage = page[midsceneAgentKeyId];
|
|
@@ -130,21 +95,17 @@ const PlaywrightAiFixture = (options)=>{
|
|
|
130
95
|
}
|
|
131
96
|
const updateDumpAnnotation = (test, dump, pageId)=>{
|
|
132
97
|
const oldTempFilePath = pageTempFiles.get(pageId);
|
|
133
|
-
if (oldTempFilePath) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
});
|
|
139
|
-
} catch (error) {}
|
|
140
|
-
}
|
|
98
|
+
if (oldTempFilePath) try {
|
|
99
|
+
rmSync(oldTempFilePath, {
|
|
100
|
+
force: true
|
|
101
|
+
});
|
|
102
|
+
} catch (error) {}
|
|
141
103
|
const tempFileName = `midscene-dump-${test.testId || uuid()}-${pageId}.json`;
|
|
142
104
|
const tempFilePath = join(tmpdir(), tempFileName);
|
|
143
105
|
try {
|
|
144
106
|
writeFileSync(tempFilePath, dump, 'utf-8');
|
|
145
107
|
debugPage(`Dump written to temp file: ${tempFilePath}`);
|
|
146
108
|
pageTempFiles.set(pageId, tempFilePath);
|
|
147
|
-
globalTempFiles.add(tempFilePath);
|
|
148
109
|
const currentAnnotation = test.annotations.find((item)=>item.type === midsceneDumpAnnotationId);
|
|
149
110
|
if (currentAnnotation) currentAnnotation.description = tempFilePath;
|
|
150
111
|
else test.annotations.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright/ai-fixture.mjs","sources":["webpack://@midscene/web/./src/playwright/ai-fixture.ts"],"sourcesContent":["import { rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { PlaywrightAgent, type PlaywrightWebPage } from '@/playwright/index';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport type { Cache } from '@midscene/core';\nimport type { AgentOpt, Agent as PageAgent } from '@midscene/core/agent';\nimport { processCacheConfig } from '@midscene/core/utils';\nimport {\n DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n} from '@midscene/shared/constants';\nimport { getDebug } from '@midscene/shared/logger';\nimport { uuid } from '@midscene/shared/utils';\nimport { replaceIllegalPathCharsAndSpace } from '@midscene/shared/utils';\nimport { type TestInfo, type TestType, test } from '@playwright/test';\nimport type { Page as OriginPlaywrightPage } from 'playwright';\nexport type APITestType = Pick<TestType<any, any>, 'step'>;\n\nconst debugPage = getDebug('web:playwright:ai-fixture');\n\nconst groupAndCaseForTest = (testInfo: TestInfo) => {\n let taskFile: string;\n let taskTitle: string;\n const titlePath = [...testInfo.titlePath];\n\n if (titlePath.length > 1) {\n taskFile = titlePath.shift() || 'unnamed';\n taskTitle = titlePath.join('__');\n } else if (titlePath.length === 1) {\n taskTitle = titlePath[0];\n taskFile = `${taskTitle}`;\n } else {\n taskTitle = 'unnamed';\n taskFile = 'unnamed';\n }\n\n const taskTitleWithRetry = `${taskTitle}${testInfo.retry ? `(retry #${testInfo.retry})` : ''}`;\n\n return {\n file: taskFile,\n id: replaceIllegalPathCharsAndSpace(`${taskFile}(${taskTitle})`),\n title: replaceIllegalPathCharsAndSpace(taskTitleWithRetry),\n };\n};\n\nconst midsceneAgentKeyId = '_midsceneAgentId';\nexport const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';\n\n// Global tracking of temporary dump files for cleanup\nconst globalTempFiles = new Set<string>();\nlet cleanupHandlersRegistered = false;\nlet cleanupComplete = false;\n\n// Register process exit handlers to clean up temp files\nfunction registerCleanupHandlers() {\n if (cleanupHandlersRegistered) return;\n cleanupHandlersRegistered = true;\n\n const cleanup = () => {\n // Prevent duplicate cleanup if already run\n if (cleanupComplete) return;\n cleanupComplete = true;\n\n debugPage(`Cleaning up ${globalTempFiles.size} temporary dump files`);\n\n // Convert Set to array to avoid iteration issues while deleting\n const filesToClean = Array.from(globalTempFiles);\n for (const filePath of filesToClean) {\n try {\n rmSync(filePath, { force: true });\n } catch (error) {\n // Silently ignore errors during cleanup\n debugPage(`Failed to clean up temp file: ${filePath}`, error);\n }\n }\n\n // Clear the Set after all files are processed\n globalTempFiles.clear();\n };\n\n // Register cleanup on process exit\n process.once('SIGINT', cleanup);\n process.once('SIGTERM', cleanup);\n process.once('SIGHUP', cleanup);\n process.once('exit', cleanup);\n process.once('beforeExit', cleanup);\n\n // Handle uncaught exceptions and unhandled rejections\n process.once('uncaughtException', (error) => {\n debugPage('Uncaught exception detected, cleaning up temp files', error);\n cleanup();\n // Don't re-throw - let the process handle it normally\n });\n\n process.once('unhandledRejection', (reason) => {\n debugPage('Unhandled rejection detected, cleaning up temp files', reason);\n cleanup();\n // Don't re-throw - let the process handle it normally\n });\n}\n\ntype PlaywrightCacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id?: string;\n};\ntype PlaywrightCache = false | true | PlaywrightCacheConfig;\n\nexport const PlaywrightAiFixture = (options?: {\n forceSameTabNavigation?: boolean;\n waitForNetworkIdleTimeout?: number;\n waitForNavigationTimeout?: number;\n cache?: PlaywrightCache;\n}) => {\n const {\n forceSameTabNavigation = true,\n waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n cache,\n } = options ?? {};\n\n // Helper function to process cache configuration and auto-generate ID from test info\n const processTestCacheConfig = (testInfo: TestInfo): Cache | undefined => {\n // Generate ID from test info\n const { id } = groupAndCaseForTest(testInfo);\n\n // Use shared processCacheConfig with generated ID as fallback\n return processCacheConfig(cache as Cache, id);\n };\n\n // Track temporary dump files for each page\n const pageTempFiles = new Map<string, string>(); // pageId -> tempFilePath\n\n // Register global cleanup handlers\n registerCleanupHandlers();\n\n const pageAgentMap: Record<string, PageAgent<PlaywrightWebPage>> = {};\n const createOrReuseAgentForPage = (\n page: OriginPlaywrightPage,\n testInfo: TestInfo, // { testId: string; taskFile: string; taskTitle: string },\n opts?: WebPageAgentOpt,\n ) => {\n let idForPage = (page as any)[midsceneAgentKeyId];\n if (!idForPage) {\n idForPage = uuid();\n (page as any)[midsceneAgentKeyId] = idForPage;\n const { testId } = testInfo;\n const { file, title } = groupAndCaseForTest(testInfo);\n const cacheConfig = processTestCacheConfig(testInfo);\n\n pageAgentMap[idForPage] = new PlaywrightAgent(page, {\n testId: `playwright-${testId}-${idForPage}`,\n forceSameTabNavigation,\n cache: cacheConfig,\n groupName: title,\n groupDescription: file,\n generateReport: false, // we will generate it in the reporter\n ...opts,\n });\n\n pageAgentMap[idForPage].onDumpUpdate = (dump: string) => {\n updateDumpAnnotation(testInfo, dump, idForPage);\n };\n\n page.on('close', () => {\n debugPage('page closed');\n\n // Note: We don't clean up temp files here because the reporter\n // needs to read them in onTestEnd. The reporter will clean them up\n // after reading. If the test is interrupted (Ctrl+C), the process\n // exit handlers will clean up remaining temp files.\n\n // However, we do clean up the pageTempFiles Map entry to avoid memory leaks\n pageTempFiles.delete(idForPage);\n\n pageAgentMap[idForPage].destroy();\n delete pageAgentMap[idForPage];\n });\n }\n\n return pageAgentMap[idForPage];\n };\n\n async function generateAiFunction(options: {\n page: OriginPlaywrightPage;\n testInfo: TestInfo;\n use: any;\n aiActionType:\n | 'ai'\n | 'aiAct'\n | 'aiHover'\n | 'aiInput'\n | 'aiKeyboardPress'\n | 'aiScroll'\n | 'aiTap'\n | 'aiRightClick'\n | 'aiDoubleClick'\n | 'aiQuery'\n | 'aiAssert'\n | 'aiWaitFor'\n | 'aiLocate'\n | 'aiNumber'\n | 'aiString'\n | 'aiBoolean'\n | 'aiAsk'\n | 'runYaml'\n | 'setAIActionContext'\n | 'evaluateJavaScript'\n | 'recordToReport'\n | 'logScreenshot'\n | 'freezePageContext'\n | 'unfreezePageContext';\n }) {\n const { page, testInfo, use, aiActionType } = options;\n const agent = createOrReuseAgentForPage(page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n }) as PlaywrightAgent;\n\n await use(async (taskPrompt: string, ...args: any[]) => {\n return new Promise((resolve, reject) => {\n test.step(`ai-${aiActionType} - ${JSON.stringify(taskPrompt)}`, async () => {\n try {\n debugPage(\n `waitForNetworkIdle timeout: ${waitForNetworkIdleTimeout}`,\n );\n await agent.waitForNetworkIdle(waitForNetworkIdleTimeout);\n } catch (error) {\n console.warn(\n '[midscene:warning] Waiting for network idle has timed out, but Midscene will continue execution. Please check https://midscenejs.com/faq.html#customize-the-network-timeout for more information on customizing the network timeout',\n );\n }\n try {\n type AgentMethod = (\n prompt: string,\n ...restArgs: any[]\n ) => Promise<any>;\n const result = await (agent[aiActionType] as AgentMethod)(\n taskPrompt,\n ...(args || []),\n );\n resolve(result);\n } catch (error) {\n reject(error);\n }\n });\n });\n });\n }\n\n const updateDumpAnnotation = (\n test: TestInfo,\n dump: string,\n pageId: string,\n ) => {\n // 1. First, clean up the old temp file if it exists\n const oldTempFilePath = pageTempFiles.get(pageId);\n if (oldTempFilePath) {\n // Remove old temp file from tracking and try to delete it\n globalTempFiles.delete(oldTempFilePath);\n try {\n rmSync(oldTempFilePath, { force: true });\n } catch (error) {\n // Silently ignore if old file is already cleaned up\n }\n }\n\n // 2. Create new temp file with predictable name using pageId\n const tempFileName = `midscene-dump-${test.testId || uuid()}-${pageId}.json`;\n const tempFilePath = join(tmpdir(), tempFileName);\n\n // 3. Write dump to the new temporary file\n try {\n writeFileSync(tempFilePath, dump, 'utf-8');\n debugPage(`Dump written to temp file: ${tempFilePath}`);\n\n // 4. Track the new temp file (only if write succeeded)\n pageTempFiles.set(pageId, tempFilePath);\n globalTempFiles.add(tempFilePath);\n\n // Store only the file path in annotation (only if write succeeded)\n const currentAnnotation = test.annotations.find((item) => {\n return item.type === midsceneDumpAnnotationId;\n });\n if (currentAnnotation) {\n // Store file path instead of dump content\n currentAnnotation.description = tempFilePath;\n } else {\n test.annotations.push({\n type: midsceneDumpAnnotationId,\n description: tempFilePath,\n });\n }\n } catch (error) {\n // If write fails (e.g., disk full), don't track the file or add annotation\n // This prevents reporter from trying to read a non-existent file\n debugPage(\n `Failed to write temp file: ${tempFilePath}. Skipping annotation.`,\n error,\n );\n }\n };\n\n return {\n agentForPage: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await use(\n async (\n propsPage?: OriginPlaywrightPage | undefined,\n opts?: AgentOpt,\n ) => {\n const cacheConfig = processTestCacheConfig(testInfo);\n\n // Handle cache configuration priority:\n // 1. If user provides cache in opts, use it (but auto-generate ID if missing)\n // 2. Otherwise use fixture's cache config\n let finalCacheConfig = cacheConfig;\n if (opts?.cache !== undefined) {\n const userCache = opts.cache;\n if (userCache === false) {\n finalCacheConfig = false;\n } else if (userCache === true) {\n // Auto-generate ID for user's cache: true\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { id };\n } else if (typeof userCache === 'object') {\n if (!userCache.id) {\n // Auto-generate ID for user's cache object without ID\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { ...userCache, id };\n } else {\n finalCacheConfig = userCache;\n }\n }\n }\n\n const agent = createOrReuseAgentForPage(propsPage || page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n cache: finalCacheConfig,\n ...opts,\n });\n return agent;\n },\n );\n },\n ai: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'ai',\n });\n },\n aiAct: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAct',\n });\n },\n aiTap: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiTap',\n });\n },\n aiRightClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiRightClick',\n });\n },\n aiDoubleClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiDoubleClick',\n });\n },\n aiHover: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiHover',\n });\n },\n aiInput: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiInput',\n });\n },\n aiKeyboardPress: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiKeyboardPress',\n });\n },\n aiScroll: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiScroll',\n });\n },\n aiQuery: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiQuery',\n });\n },\n aiAssert: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAssert',\n });\n },\n aiWaitFor: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiWaitFor',\n });\n },\n aiLocate: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiLocate',\n });\n },\n aiNumber: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiNumber',\n });\n },\n aiString: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiString',\n });\n },\n aiBoolean: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiBoolean',\n });\n },\n aiAsk: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAsk',\n });\n },\n runYaml: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'runYaml',\n });\n },\n setAIActionContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'setAIActionContext',\n });\n },\n evaluateJavaScript: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'evaluateJavaScript',\n });\n },\n recordToReport: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'recordToReport',\n });\n },\n logScreenshot: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'logScreenshot',\n });\n },\n freezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'freezePageContext',\n });\n },\n unfreezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'unfreezePageContext',\n });\n },\n };\n};\n\nexport type PlayWrightAiFixtureType = {\n agentForPage: (\n page?: any,\n opts?: any,\n ) => Promise<PageAgent<PlaywrightWebPage>>;\n ai: <T = any>(prompt: string) => Promise<T>;\n aiAct: (taskPrompt: string) => ReturnType<PageAgent['aiAct']>;\n aiTap: (\n ...args: Parameters<PageAgent['aiTap']>\n ) => ReturnType<PageAgent['aiTap']>;\n aiRightClick: (\n ...args: Parameters<PageAgent['aiRightClick']>\n ) => ReturnType<PageAgent['aiRightClick']>;\n aiDoubleClick: (\n ...args: Parameters<PageAgent['aiDoubleClick']>\n ) => ReturnType<PageAgent['aiDoubleClick']>;\n aiHover: (\n ...args: Parameters<PageAgent['aiHover']>\n ) => ReturnType<PageAgent['aiHover']>;\n aiInput: (\n ...args: Parameters<PageAgent['aiInput']>\n ) => ReturnType<PageAgent['aiInput']>;\n aiKeyboardPress: (\n ...args: Parameters<PageAgent['aiKeyboardPress']>\n ) => ReturnType<PageAgent['aiKeyboardPress']>;\n aiScroll: (\n ...args: Parameters<PageAgent['aiScroll']>\n ) => ReturnType<PageAgent['aiScroll']>;\n aiQuery: <T = any>(...args: Parameters<PageAgent['aiQuery']>) => Promise<T>;\n aiAssert: (\n ...args: Parameters<PageAgent['aiAssert']>\n ) => ReturnType<PageAgent['aiAssert']>;\n aiWaitFor: (...args: Parameters<PageAgent['aiWaitFor']>) => Promise<void>;\n aiLocate: (\n ...args: Parameters<PageAgent['aiLocate']>\n ) => ReturnType<PageAgent['aiLocate']>;\n aiNumber: (\n ...args: Parameters<PageAgent['aiNumber']>\n ) => ReturnType<PageAgent['aiNumber']>;\n aiString: (\n ...args: Parameters<PageAgent['aiString']>\n ) => ReturnType<PageAgent['aiString']>;\n aiBoolean: (\n ...args: Parameters<PageAgent['aiBoolean']>\n ) => ReturnType<PageAgent['aiBoolean']>;\n aiAsk: (\n ...args: Parameters<PageAgent['aiAsk']>\n ) => ReturnType<PageAgent['aiAsk']>;\n runYaml: (\n ...args: Parameters<PageAgent['runYaml']>\n ) => ReturnType<PageAgent['runYaml']>;\n setAIActionContext: (\n ...args: Parameters<PageAgent['setAIActionContext']>\n ) => ReturnType<PageAgent['setAIActionContext']>;\n evaluateJavaScript: (\n ...args: Parameters<PageAgent['evaluateJavaScript']>\n ) => ReturnType<PageAgent['evaluateJavaScript']>;\n recordToReport: (\n ...args: Parameters<PageAgent['recordToReport']>\n ) => ReturnType<PageAgent['recordToReport']>;\n logScreenshot: (\n ...args: Parameters<PageAgent['logScreenshot']>\n ) => ReturnType<PageAgent['logScreenshot']>;\n freezePageContext: (\n ...args: Parameters<PageAgent['freezePageContext']>\n ) => ReturnType<PageAgent['freezePageContext']>;\n unfreezePageContext: (\n ...args: Parameters<PageAgent['unfreezePageContext']>\n ) => ReturnType<PageAgent['unfreezePageContext']>;\n};\n"],"names":["debugPage","getDebug","groupAndCaseForTest","testInfo","taskFile","taskTitle","titlePath","taskTitleWithRetry","replaceIllegalPathCharsAndSpace","midsceneAgentKeyId","midsceneDumpAnnotationId","globalTempFiles","Set","cleanupHandlersRegistered","cleanupComplete","registerCleanupHandlers","cleanup","filesToClean","Array","filePath","rmSync","error","process","reason","PlaywrightAiFixture","options","forceSameTabNavigation","waitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","waitForNavigationTimeout","DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT","cache","processTestCacheConfig","id","processCacheConfig","pageTempFiles","Map","pageAgentMap","createOrReuseAgentForPage","page","opts","idForPage","uuid","testId","file","title","cacheConfig","PlaywrightAgent","dump","updateDumpAnnotation","generateAiFunction","use","aiActionType","agent","taskPrompt","args","Promise","resolve","reject","test","JSON","console","result","pageId","oldTempFilePath","tempFileName","tempFilePath","join","tmpdir","writeFileSync","currentAnnotation","item","propsPage","finalCacheConfig","undefined","userCache"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"playwright/ai-fixture.mjs","sources":["webpack://@midscene/web/./src/playwright/ai-fixture.ts"],"sourcesContent":["import { rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { PlaywrightAgent, type PlaywrightWebPage } from '@/playwright/index';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport type { Cache } from '@midscene/core';\nimport type { AgentOpt, Agent as PageAgent } from '@midscene/core/agent';\nimport { processCacheConfig } from '@midscene/core/utils';\nimport {\n DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n} from '@midscene/shared/constants';\nimport { getDebug } from '@midscene/shared/logger';\nimport { uuid } from '@midscene/shared/utils';\nimport { replaceIllegalPathCharsAndSpace } from '@midscene/shared/utils';\nimport { type TestInfo, type TestType, test } from '@playwright/test';\nimport type { Page as OriginPlaywrightPage } from 'playwright';\nexport type APITestType = Pick<TestType<any, any>, 'step'>;\n\nconst debugPage = getDebug('web:playwright:ai-fixture');\n\nconst groupAndCaseForTest = (testInfo: TestInfo) => {\n let taskFile: string;\n let taskTitle: string;\n const titlePath = [...testInfo.titlePath];\n\n if (titlePath.length > 1) {\n taskFile = titlePath.shift() || 'unnamed';\n taskTitle = titlePath.join('__');\n } else if (titlePath.length === 1) {\n taskTitle = titlePath[0];\n taskFile = `${taskTitle}`;\n } else {\n taskTitle = 'unnamed';\n taskFile = 'unnamed';\n }\n\n const taskTitleWithRetry = `${taskTitle}${testInfo.retry ? `(retry #${testInfo.retry})` : ''}`;\n\n return {\n file: taskFile,\n id: replaceIllegalPathCharsAndSpace(`${taskFile}(${taskTitle})`),\n title: replaceIllegalPathCharsAndSpace(taskTitleWithRetry),\n };\n};\n\nconst midsceneAgentKeyId = '_midsceneAgentId';\nexport const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';\n\n// Track temporary dump files per page for cleanup\nconst pageTempFiles = new Map<string, string>();\n\ntype PlaywrightCacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id?: string;\n};\ntype PlaywrightCache = false | true | PlaywrightCacheConfig;\n\nexport const PlaywrightAiFixture = (options?: {\n forceSameTabNavigation?: boolean;\n waitForNetworkIdleTimeout?: number;\n waitForNavigationTimeout?: number;\n cache?: PlaywrightCache;\n}) => {\n const {\n forceSameTabNavigation = true,\n waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n cache,\n } = options ?? {};\n\n // Helper function to process cache configuration and auto-generate ID from test info\n const processTestCacheConfig = (testInfo: TestInfo): Cache | undefined => {\n // Generate ID from test info\n const { id } = groupAndCaseForTest(testInfo);\n\n // Use shared processCacheConfig with generated ID as fallback\n return processCacheConfig(cache as Cache, id);\n };\n\n const pageAgentMap: Record<string, PageAgent<PlaywrightWebPage>> = {};\n const createOrReuseAgentForPage = (\n page: OriginPlaywrightPage,\n testInfo: TestInfo, // { testId: string; taskFile: string; taskTitle: string },\n opts?: WebPageAgentOpt,\n ) => {\n let idForPage = (page as any)[midsceneAgentKeyId];\n if (!idForPage) {\n idForPage = uuid();\n (page as any)[midsceneAgentKeyId] = idForPage;\n const { testId } = testInfo;\n const { file, title } = groupAndCaseForTest(testInfo);\n const cacheConfig = processTestCacheConfig(testInfo);\n\n pageAgentMap[idForPage] = new PlaywrightAgent(page, {\n testId: `playwright-${testId}-${idForPage}`,\n forceSameTabNavigation,\n cache: cacheConfig,\n groupName: title,\n groupDescription: file,\n generateReport: false, // we will generate it in the reporter\n ...opts,\n });\n\n pageAgentMap[idForPage].onDumpUpdate = (dump: string) => {\n updateDumpAnnotation(testInfo, dump, idForPage);\n };\n\n page.on('close', () => {\n debugPage('page closed');\n\n // Note: We don't clean up temp files here because the reporter\n // needs to read them in onTestEnd. The reporter will clean them up\n // after reading. If the test is interrupted (Ctrl+C), the process\n // exit handlers will clean up remaining temp files.\n\n // However, we do clean up the pageTempFiles Map entry to avoid memory leaks\n pageTempFiles.delete(idForPage);\n\n pageAgentMap[idForPage].destroy();\n delete pageAgentMap[idForPage];\n });\n }\n\n return pageAgentMap[idForPage];\n };\n\n async function generateAiFunction(options: {\n page: OriginPlaywrightPage;\n testInfo: TestInfo;\n use: any;\n aiActionType:\n | 'ai'\n | 'aiAct'\n | 'aiHover'\n | 'aiInput'\n | 'aiKeyboardPress'\n | 'aiScroll'\n | 'aiTap'\n | 'aiRightClick'\n | 'aiDoubleClick'\n | 'aiQuery'\n | 'aiAssert'\n | 'aiWaitFor'\n | 'aiLocate'\n | 'aiNumber'\n | 'aiString'\n | 'aiBoolean'\n | 'aiAsk'\n | 'runYaml'\n | 'setAIActionContext'\n | 'evaluateJavaScript'\n | 'recordToReport'\n | 'logScreenshot'\n | 'freezePageContext'\n | 'unfreezePageContext';\n }) {\n const { page, testInfo, use, aiActionType } = options;\n const agent = createOrReuseAgentForPage(page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n }) as PlaywrightAgent;\n\n await use(async (taskPrompt: string, ...args: any[]) => {\n return new Promise((resolve, reject) => {\n test.step(`ai-${aiActionType} - ${JSON.stringify(taskPrompt)}`, async () => {\n try {\n debugPage(\n `waitForNetworkIdle timeout: ${waitForNetworkIdleTimeout}`,\n );\n await agent.waitForNetworkIdle(waitForNetworkIdleTimeout);\n } catch (error) {\n console.warn(\n '[midscene:warning] Waiting for network idle has timed out, but Midscene will continue execution. Please check https://midscenejs.com/faq.html#customize-the-network-timeout for more information on customizing the network timeout',\n );\n }\n try {\n type AgentMethod = (\n prompt: string,\n ...restArgs: any[]\n ) => Promise<any>;\n const result = await (agent[aiActionType] as AgentMethod)(\n taskPrompt,\n ...(args || []),\n );\n resolve(result);\n } catch (error) {\n reject(error);\n }\n });\n });\n });\n }\n\n const updateDumpAnnotation = (\n test: TestInfo,\n dump: string,\n pageId: string,\n ) => {\n // 1. First, clean up the old temp file if it exists\n const oldTempFilePath = pageTempFiles.get(pageId);\n if (oldTempFilePath) {\n try {\n rmSync(oldTempFilePath, { force: true });\n } catch (error) {\n // Silently ignore if old file is already cleaned up\n }\n }\n\n // 2. Create new temp file with predictable name using pageId\n const tempFileName = `midscene-dump-${test.testId || uuid()}-${pageId}.json`;\n const tempFilePath = join(tmpdir(), tempFileName);\n\n // 3. Write dump to the new temporary file\n try {\n writeFileSync(tempFilePath, dump, 'utf-8');\n debugPage(`Dump written to temp file: ${tempFilePath}`);\n\n // 4. Track the new temp file (only if write succeeded)\n pageTempFiles.set(pageId, tempFilePath);\n\n // Store only the file path in annotation (only if write succeeded)\n const currentAnnotation = test.annotations.find((item) => {\n return item.type === midsceneDumpAnnotationId;\n });\n if (currentAnnotation) {\n // Store file path instead of dump content\n currentAnnotation.description = tempFilePath;\n } else {\n test.annotations.push({\n type: midsceneDumpAnnotationId,\n description: tempFilePath,\n });\n }\n } catch (error) {\n // If write fails (e.g., disk full), don't track the file or add annotation\n // This prevents reporter from trying to read a non-existent file\n debugPage(\n `Failed to write temp file: ${tempFilePath}. Skipping annotation.`,\n error,\n );\n }\n };\n\n return {\n agentForPage: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await use(\n async (\n propsPage?: OriginPlaywrightPage | undefined,\n opts?: AgentOpt,\n ) => {\n const cacheConfig = processTestCacheConfig(testInfo);\n\n // Handle cache configuration priority:\n // 1. If user provides cache in opts, use it (but auto-generate ID if missing)\n // 2. Otherwise use fixture's cache config\n let finalCacheConfig = cacheConfig;\n if (opts?.cache !== undefined) {\n const userCache = opts.cache;\n if (userCache === false) {\n finalCacheConfig = false;\n } else if (userCache === true) {\n // Auto-generate ID for user's cache: true\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { id };\n } else if (typeof userCache === 'object') {\n if (!userCache.id) {\n // Auto-generate ID for user's cache object without ID\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { ...userCache, id };\n } else {\n finalCacheConfig = userCache;\n }\n }\n }\n\n const agent = createOrReuseAgentForPage(propsPage || page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n cache: finalCacheConfig,\n ...opts,\n });\n return agent;\n },\n );\n },\n ai: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'ai',\n });\n },\n aiAct: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAct',\n });\n },\n aiTap: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiTap',\n });\n },\n aiRightClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiRightClick',\n });\n },\n aiDoubleClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiDoubleClick',\n });\n },\n aiHover: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiHover',\n });\n },\n aiInput: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiInput',\n });\n },\n aiKeyboardPress: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiKeyboardPress',\n });\n },\n aiScroll: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiScroll',\n });\n },\n aiQuery: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiQuery',\n });\n },\n aiAssert: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAssert',\n });\n },\n aiWaitFor: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiWaitFor',\n });\n },\n aiLocate: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiLocate',\n });\n },\n aiNumber: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiNumber',\n });\n },\n aiString: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiString',\n });\n },\n aiBoolean: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiBoolean',\n });\n },\n aiAsk: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAsk',\n });\n },\n runYaml: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'runYaml',\n });\n },\n setAIActionContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'setAIActionContext',\n });\n },\n evaluateJavaScript: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'evaluateJavaScript',\n });\n },\n recordToReport: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'recordToReport',\n });\n },\n logScreenshot: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'logScreenshot',\n });\n },\n freezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'freezePageContext',\n });\n },\n unfreezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'unfreezePageContext',\n });\n },\n };\n};\n\nexport type PlayWrightAiFixtureType = {\n agentForPage: (\n page?: any,\n opts?: any,\n ) => Promise<PageAgent<PlaywrightWebPage>>;\n ai: <T = any>(prompt: string) => Promise<T>;\n aiAct: (taskPrompt: string) => ReturnType<PageAgent['aiAct']>;\n aiTap: (\n ...args: Parameters<PageAgent['aiTap']>\n ) => ReturnType<PageAgent['aiTap']>;\n aiRightClick: (\n ...args: Parameters<PageAgent['aiRightClick']>\n ) => ReturnType<PageAgent['aiRightClick']>;\n aiDoubleClick: (\n ...args: Parameters<PageAgent['aiDoubleClick']>\n ) => ReturnType<PageAgent['aiDoubleClick']>;\n aiHover: (\n ...args: Parameters<PageAgent['aiHover']>\n ) => ReturnType<PageAgent['aiHover']>;\n aiInput: (\n ...args: Parameters<PageAgent['aiInput']>\n ) => ReturnType<PageAgent['aiInput']>;\n aiKeyboardPress: (\n ...args: Parameters<PageAgent['aiKeyboardPress']>\n ) => ReturnType<PageAgent['aiKeyboardPress']>;\n aiScroll: (\n ...args: Parameters<PageAgent['aiScroll']>\n ) => ReturnType<PageAgent['aiScroll']>;\n aiQuery: <T = any>(...args: Parameters<PageAgent['aiQuery']>) => Promise<T>;\n aiAssert: (\n ...args: Parameters<PageAgent['aiAssert']>\n ) => ReturnType<PageAgent['aiAssert']>;\n aiWaitFor: (...args: Parameters<PageAgent['aiWaitFor']>) => Promise<void>;\n aiLocate: (\n ...args: Parameters<PageAgent['aiLocate']>\n ) => ReturnType<PageAgent['aiLocate']>;\n aiNumber: (\n ...args: Parameters<PageAgent['aiNumber']>\n ) => ReturnType<PageAgent['aiNumber']>;\n aiString: (\n ...args: Parameters<PageAgent['aiString']>\n ) => ReturnType<PageAgent['aiString']>;\n aiBoolean: (\n ...args: Parameters<PageAgent['aiBoolean']>\n ) => ReturnType<PageAgent['aiBoolean']>;\n aiAsk: (\n ...args: Parameters<PageAgent['aiAsk']>\n ) => ReturnType<PageAgent['aiAsk']>;\n runYaml: (\n ...args: Parameters<PageAgent['runYaml']>\n ) => ReturnType<PageAgent['runYaml']>;\n setAIActionContext: (\n ...args: Parameters<PageAgent['setAIActionContext']>\n ) => ReturnType<PageAgent['setAIActionContext']>;\n evaluateJavaScript: (\n ...args: Parameters<PageAgent['evaluateJavaScript']>\n ) => ReturnType<PageAgent['evaluateJavaScript']>;\n recordToReport: (\n ...args: Parameters<PageAgent['recordToReport']>\n ) => ReturnType<PageAgent['recordToReport']>;\n logScreenshot: (\n ...args: Parameters<PageAgent['logScreenshot']>\n ) => ReturnType<PageAgent['logScreenshot']>;\n freezePageContext: (\n ...args: Parameters<PageAgent['freezePageContext']>\n ) => ReturnType<PageAgent['freezePageContext']>;\n unfreezePageContext: (\n ...args: Parameters<PageAgent['unfreezePageContext']>\n ) => ReturnType<PageAgent['unfreezePageContext']>;\n};\n"],"names":["debugPage","getDebug","groupAndCaseForTest","testInfo","taskFile","taskTitle","titlePath","taskTitleWithRetry","replaceIllegalPathCharsAndSpace","midsceneAgentKeyId","midsceneDumpAnnotationId","pageTempFiles","Map","PlaywrightAiFixture","options","forceSameTabNavigation","waitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","waitForNavigationTimeout","DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT","cache","processTestCacheConfig","id","processCacheConfig","pageAgentMap","createOrReuseAgentForPage","page","opts","idForPage","uuid","testId","file","title","cacheConfig","PlaywrightAgent","dump","updateDumpAnnotation","generateAiFunction","use","aiActionType","agent","taskPrompt","args","Promise","resolve","reject","test","JSON","error","console","result","pageId","oldTempFilePath","rmSync","tempFileName","tempFilePath","join","tmpdir","writeFileSync","currentAnnotation","item","propsPage","finalCacheConfig","undefined","userCache"],"mappings":"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"}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { readFileSync,
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
1
|
+
import { readFileSync, rmSync } from "node:fs";
|
|
4
2
|
import { getReportFileName, printReportMsg } from "@midscene/core/agent";
|
|
5
3
|
import { writeDumpReport } from "@midscene/core/utils";
|
|
6
4
|
import { replaceIllegalPathCharsAndSpace } from "@midscene/shared/utils";
|
|
@@ -52,6 +50,7 @@ class MidsceneReporter {
|
|
|
52
50
|
const dumpAnnotation = test.annotations.find((annotation)=>'MIDSCENE_DUMP_ANNOTATION' === annotation.type);
|
|
53
51
|
if (!(null == dumpAnnotation ? void 0 : dumpAnnotation.description)) return;
|
|
54
52
|
const tempFilePath = dumpAnnotation.description;
|
|
53
|
+
this.tempFiles.add(tempFilePath);
|
|
55
54
|
let dumpString;
|
|
56
55
|
try {
|
|
57
56
|
dumpString = readFileSync(tempFilePath, 'utf-8');
|
|
@@ -76,38 +75,27 @@ class MidsceneReporter {
|
|
|
76
75
|
rmSync(tempFilePath, {
|
|
77
76
|
force: true
|
|
78
77
|
});
|
|
78
|
+
this.tempFiles.delete(tempFilePath);
|
|
79
79
|
} catch (error) {
|
|
80
80
|
console.warn(`Failed to delete Midscene temp file: ${tempFilePath}`, error);
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
console.log(`Midscene: Found ${orphanedFiles.length} orphaned temp file(s), cleaning up...`);
|
|
93
|
-
for (const file of orphanedFiles){
|
|
94
|
-
const filePath = join(tmpDir, file);
|
|
95
|
-
try {
|
|
96
|
-
rmSync(filePath, {
|
|
97
|
-
force: true
|
|
98
|
-
});
|
|
99
|
-
console.log(`Midscene: Cleaned up orphaned temp file: ${file}`);
|
|
100
|
-
} catch (error) {}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
} catch (error) {
|
|
104
|
-
console.warn('Midscene: Failed to scan for orphaned temp files:', error);
|
|
83
|
+
onEnd() {
|
|
84
|
+
if (this.tempFiles.size > 0) {
|
|
85
|
+
console.log(`Midscene: Cleaning up ${this.tempFiles.size} remaining temp file(s)...`);
|
|
86
|
+
for (const filePath of this.tempFiles)try {
|
|
87
|
+
rmSync(filePath, {
|
|
88
|
+
force: true
|
|
89
|
+
});
|
|
90
|
+
} catch (error) {}
|
|
91
|
+
this.tempFiles.clear();
|
|
105
92
|
}
|
|
106
93
|
}
|
|
107
94
|
constructor(options = {}){
|
|
108
95
|
_define_property(this, "mergedFilename", void 0);
|
|
109
96
|
_define_property(this, "testTitleToFilename", new Map());
|
|
110
97
|
_define_property(this, "mode", void 0);
|
|
98
|
+
_define_property(this, "tempFiles", new Set());
|
|
111
99
|
this.mode = MidsceneReporter.getMode(options.type ?? 'merged');
|
|
112
100
|
}
|
|
113
101
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright/reporter/index.mjs","sources":["webpack://@midscene/web/./src/playwright/reporter/index.ts"],"sourcesContent":["import { readFileSync,
|
|
1
|
+
{"version":3,"file":"playwright/reporter/index.mjs","sources":["webpack://@midscene/web/./src/playwright/reporter/index.ts"],"sourcesContent":["import { readFileSync, rmSync } from 'node:fs';\nimport type { ReportDumpWithAttributes } from '@midscene/core';\nimport { getReportFileName, printReportMsg } from '@midscene/core/agent';\nimport { writeDumpReport } from '@midscene/core/utils';\nimport { replaceIllegalPathCharsAndSpace } from '@midscene/shared/utils';\nimport type {\n FullConfig,\n Reporter,\n Suite,\n TestCase,\n TestResult,\n} from '@playwright/test/reporter';\n\ninterface MidsceneReporterOptions {\n type?: 'merged' | 'separate';\n}\n\nclass MidsceneReporter implements Reporter {\n private mergedFilename?: string;\n private testTitleToFilename = new Map<string, string>();\n mode?: 'merged' | 'separate';\n\n // Track all temp files created during this test run for cleanup\n private tempFiles = new Set<string>();\n\n constructor(options: MidsceneReporterOptions = {}) {\n // Set mode from constructor options (official Playwright way)\n this.mode = MidsceneReporter.getMode(options.type ?? 'merged');\n }\n\n private static getMode(reporterType: string): 'merged' | 'separate' {\n if (!reporterType) {\n return 'merged';\n }\n if (reporterType !== 'merged' && reporterType !== 'separate') {\n throw new Error(\n `Unknown reporter type in playwright config: ${reporterType}, only support 'merged' or 'separate'`,\n );\n }\n return reporterType;\n }\n\n private getSeparatedFilename(testTitle: string): string {\n if (!this.testTitleToFilename.has(testTitle)) {\n const baseTag = `playwright-${replaceIllegalPathCharsAndSpace(testTitle)}`;\n const generatedFilename = getReportFileName(baseTag);\n this.testTitleToFilename.set(testTitle, generatedFilename);\n }\n return this.testTitleToFilename.get(testTitle)!;\n }\n\n private getReportFilename(testTitle?: string): string {\n if (this.mode === 'merged') {\n if (!this.mergedFilename) {\n this.mergedFilename = getReportFileName('playwright-merged');\n }\n return this.mergedFilename;\n } else if (this.mode === 'separate') {\n if (!testTitle) throw new Error('testTitle is required in separate mode');\n return this.getSeparatedFilename(testTitle);\n }\n throw new Error(`Unknown mode: ${this.mode}`);\n }\n\n private updateReport(testData: ReportDumpWithAttributes) {\n if (!testData || !this.mode) return;\n const fileName = this.getReportFilename(\n testData.attributes?.playwright_test_title,\n );\n const reportPath = writeDumpReport(\n fileName,\n testData,\n this.mode === 'merged',\n );\n reportPath && printReportMsg(reportPath);\n }\n\n async onBegin(config: FullConfig, suite: Suite) {}\n\n onTestBegin(_test: TestCase, _result: TestResult) {\n // logger(`Starting test ${test.title}`);\n }\n\n onTestEnd(test: TestCase, result: TestResult) {\n const dumpAnnotation = test.annotations.find((annotation) => {\n return annotation.type === 'MIDSCENE_DUMP_ANNOTATION';\n });\n if (!dumpAnnotation?.description) return;\n\n const tempFilePath = dumpAnnotation.description;\n\n // Track this temp file for potential cleanup in onEnd\n this.tempFiles.add(tempFilePath);\n\n let dumpString: string | undefined;\n\n try {\n dumpString = readFileSync(tempFilePath, 'utf-8');\n } catch (error) {\n console.error(\n `Failed to read Midscene dump file: ${tempFilePath}`,\n error,\n );\n // Don't return here - we still need to clean up the temp file\n }\n\n // Only update report if we successfully read the dump\n if (dumpString) {\n const retry = result.retry ? `(retry #${result.retry})` : '';\n const testId = `${test.id}${retry}`;\n const testData: ReportDumpWithAttributes = {\n dumpString,\n attributes: {\n playwright_test_id: testId,\n playwright_test_title: `${test.title}${retry}`,\n playwright_test_status: result.status,\n playwright_test_duration: result.duration,\n },\n };\n\n this.updateReport(testData);\n }\n\n // Always try to clean up temp file\n try {\n rmSync(tempFilePath, { force: true });\n // If successfully deleted, remove from tracking\n this.tempFiles.delete(tempFilePath);\n } catch (error) {\n console.warn(\n `Failed to delete Midscene temp file: ${tempFilePath}`,\n error,\n );\n // Keep in tempFiles for cleanup in onEnd\n }\n }\n\n onEnd() {\n // Clean up any remaining temp files that weren't deleted in onTestEnd\n if (this.tempFiles.size > 0) {\n console.log(\n `Midscene: Cleaning up ${this.tempFiles.size} remaining temp file(s)...`,\n );\n\n for (const filePath of this.tempFiles) {\n try {\n rmSync(filePath, { force: true });\n } catch (error) {\n // Silently ignore - file may have been deleted already\n }\n }\n\n this.tempFiles.clear();\n }\n }\n}\n\nexport default MidsceneReporter;\n"],"names":["MidsceneReporter","reporterType","Error","testTitle","baseTag","replaceIllegalPathCharsAndSpace","generatedFilename","getReportFileName","testData","_testData_attributes","fileName","reportPath","writeDumpReport","printReportMsg","config","suite","_test","_result","test","result","dumpAnnotation","annotation","tempFilePath","dumpString","readFileSync","error","console","retry","testId","rmSync","filePath","options","Map","Set"],"mappings":"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"}
|
|
@@ -45,7 +45,7 @@ class BridgeClient {
|
|
|
45
45
|
this.socket = (0, external_socket_io_client_namespaceObject.io)(this.endpoint, {
|
|
46
46
|
reconnection: false,
|
|
47
47
|
query: {
|
|
48
|
-
version: "1.0.1-beta-
|
|
48
|
+
version: "1.0.1-beta-20251028121806.0"
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
const timeout = setTimeout(()=>{
|
|
@@ -104,7 +104,7 @@ class BridgeServer {
|
|
|
104
104
|
(0, shared_utils_namespaceObject.logMsg)('one client connected');
|
|
105
105
|
this.socket = socket;
|
|
106
106
|
const clientVersion = socket.handshake.query.version;
|
|
107
|
-
(0, shared_utils_namespaceObject.logMsg)(`Bridge connected, cli-side version v1.0.1-beta-
|
|
107
|
+
(0, shared_utils_namespaceObject.logMsg)(`Bridge connected, cli-side version v1.0.1-beta-20251028121806.0, browser-side version v${clientVersion}`);
|
|
108
108
|
socket.on(external_common_js_namespaceObject.BridgeEvent.CallResponse, (params)=>{
|
|
109
109
|
const id = params.id;
|
|
110
110
|
const response = params.response;
|
|
@@ -132,7 +132,7 @@ class BridgeServer {
|
|
|
132
132
|
var _this_onConnect, _this;
|
|
133
133
|
null == (_this_onConnect = (_this = this).onConnect) || _this_onConnect.call(_this);
|
|
134
134
|
const payload = {
|
|
135
|
-
version: "1.0.1-beta-
|
|
135
|
+
version: "1.0.1-beta-20251028121806.0"
|
|
136
136
|
};
|
|
137
137
|
socket.emit(external_common_js_namespaceObject.BridgeEvent.Connected, payload);
|
|
138
138
|
Promise.resolve().then(()=>{
|
|
@@ -84,7 +84,7 @@ class ExtensionBridgePageBrowserSide extends page_js_default() {
|
|
|
84
84
|
}
|
|
85
85
|
}, ()=>this.destroy());
|
|
86
86
|
await this.bridgeClient.connect();
|
|
87
|
-
this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.0.1-beta-
|
|
87
|
+
this.onLogMessage(`Bridge connected, cli-side version v${this.bridgeClient.serverVersion}, browser-side version v1.0.1-beta-20251028121806.0`, 'log');
|
|
88
88
|
}
|
|
89
89
|
async connect() {
|
|
90
90
|
return await this.setupBridgeClient();
|
|
@@ -62,48 +62,13 @@ const groupAndCaseForTest = (testInfo)=>{
|
|
|
62
62
|
};
|
|
63
63
|
const midsceneAgentKeyId = '_midsceneAgentId';
|
|
64
64
|
const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';
|
|
65
|
-
const
|
|
66
|
-
let cleanupHandlersRegistered = false;
|
|
67
|
-
let cleanupComplete = false;
|
|
68
|
-
function registerCleanupHandlers() {
|
|
69
|
-
if (cleanupHandlersRegistered) return;
|
|
70
|
-
cleanupHandlersRegistered = true;
|
|
71
|
-
const cleanup = ()=>{
|
|
72
|
-
if (cleanupComplete) return;
|
|
73
|
-
cleanupComplete = true;
|
|
74
|
-
debugPage(`Cleaning up ${globalTempFiles.size} temporary dump files`);
|
|
75
|
-
const filesToClean = Array.from(globalTempFiles);
|
|
76
|
-
for (const filePath of filesToClean)try {
|
|
77
|
-
(0, external_node_fs_namespaceObject.rmSync)(filePath, {
|
|
78
|
-
force: true
|
|
79
|
-
});
|
|
80
|
-
} catch (error) {
|
|
81
|
-
debugPage(`Failed to clean up temp file: ${filePath}`, error);
|
|
82
|
-
}
|
|
83
|
-
globalTempFiles.clear();
|
|
84
|
-
};
|
|
85
|
-
process.once('SIGINT', cleanup);
|
|
86
|
-
process.once('SIGTERM', cleanup);
|
|
87
|
-
process.once('SIGHUP', cleanup);
|
|
88
|
-
process.once('exit', cleanup);
|
|
89
|
-
process.once('beforeExit', cleanup);
|
|
90
|
-
process.once('uncaughtException', (error)=>{
|
|
91
|
-
debugPage('Uncaught exception detected, cleaning up temp files', error);
|
|
92
|
-
cleanup();
|
|
93
|
-
});
|
|
94
|
-
process.once('unhandledRejection', (reason)=>{
|
|
95
|
-
debugPage('Unhandled rejection detected, cleaning up temp files', reason);
|
|
96
|
-
cleanup();
|
|
97
|
-
});
|
|
98
|
-
}
|
|
65
|
+
const pageTempFiles = new Map();
|
|
99
66
|
const PlaywrightAiFixture = (options)=>{
|
|
100
67
|
const { forceSameTabNavigation = true, waitForNetworkIdleTimeout = constants_namespaceObject.DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT, waitForNavigationTimeout = constants_namespaceObject.DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, cache } = options ?? {};
|
|
101
68
|
const processTestCacheConfig = (testInfo)=>{
|
|
102
69
|
const { id } = groupAndCaseForTest(testInfo);
|
|
103
70
|
return (0, utils_namespaceObject.processCacheConfig)(cache, id);
|
|
104
71
|
};
|
|
105
|
-
const pageTempFiles = new Map();
|
|
106
|
-
registerCleanupHandlers();
|
|
107
72
|
const pageAgentMap = {};
|
|
108
73
|
const createOrReuseAgentForPage = (page, testInfo, opts)=>{
|
|
109
74
|
let idForPage = page[midsceneAgentKeyId];
|
|
@@ -159,21 +124,17 @@ const PlaywrightAiFixture = (options)=>{
|
|
|
159
124
|
}
|
|
160
125
|
const updateDumpAnnotation = (test, dump, pageId)=>{
|
|
161
126
|
const oldTempFilePath = pageTempFiles.get(pageId);
|
|
162
|
-
if (oldTempFilePath) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
});
|
|
168
|
-
} catch (error) {}
|
|
169
|
-
}
|
|
127
|
+
if (oldTempFilePath) try {
|
|
128
|
+
(0, external_node_fs_namespaceObject.rmSync)(oldTempFilePath, {
|
|
129
|
+
force: true
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {}
|
|
170
132
|
const tempFileName = `midscene-dump-${test.testId || (0, shared_utils_namespaceObject.uuid)()}-${pageId}.json`;
|
|
171
133
|
const tempFilePath = (0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), tempFileName);
|
|
172
134
|
try {
|
|
173
135
|
(0, external_node_fs_namespaceObject.writeFileSync)(tempFilePath, dump, 'utf-8');
|
|
174
136
|
debugPage(`Dump written to temp file: ${tempFilePath}`);
|
|
175
137
|
pageTempFiles.set(pageId, tempFilePath);
|
|
176
|
-
globalTempFiles.add(tempFilePath);
|
|
177
138
|
const currentAnnotation = test.annotations.find((item)=>item.type === midsceneDumpAnnotationId);
|
|
178
139
|
if (currentAnnotation) currentAnnotation.description = tempFilePath;
|
|
179
140
|
else test.annotations.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright/ai-fixture.js","sources":["webpack://@midscene/web/webpack/runtime/define_property_getters","webpack://@midscene/web/webpack/runtime/has_own_property","webpack://@midscene/web/webpack/runtime/make_namespace_object","webpack://@midscene/web/./src/playwright/ai-fixture.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 { rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { PlaywrightAgent, type PlaywrightWebPage } from '@/playwright/index';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport type { Cache } from '@midscene/core';\nimport type { AgentOpt, Agent as PageAgent } from '@midscene/core/agent';\nimport { processCacheConfig } from '@midscene/core/utils';\nimport {\n DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n} from '@midscene/shared/constants';\nimport { getDebug } from '@midscene/shared/logger';\nimport { uuid } from '@midscene/shared/utils';\nimport { replaceIllegalPathCharsAndSpace } from '@midscene/shared/utils';\nimport { type TestInfo, type TestType, test } from '@playwright/test';\nimport type { Page as OriginPlaywrightPage } from 'playwright';\nexport type APITestType = Pick<TestType<any, any>, 'step'>;\n\nconst debugPage = getDebug('web:playwright:ai-fixture');\n\nconst groupAndCaseForTest = (testInfo: TestInfo) => {\n let taskFile: string;\n let taskTitle: string;\n const titlePath = [...testInfo.titlePath];\n\n if (titlePath.length > 1) {\n taskFile = titlePath.shift() || 'unnamed';\n taskTitle = titlePath.join('__');\n } else if (titlePath.length === 1) {\n taskTitle = titlePath[0];\n taskFile = `${taskTitle}`;\n } else {\n taskTitle = 'unnamed';\n taskFile = 'unnamed';\n }\n\n const taskTitleWithRetry = `${taskTitle}${testInfo.retry ? `(retry #${testInfo.retry})` : ''}`;\n\n return {\n file: taskFile,\n id: replaceIllegalPathCharsAndSpace(`${taskFile}(${taskTitle})`),\n title: replaceIllegalPathCharsAndSpace(taskTitleWithRetry),\n };\n};\n\nconst midsceneAgentKeyId = '_midsceneAgentId';\nexport const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';\n\n// Global tracking of temporary dump files for cleanup\nconst globalTempFiles = new Set<string>();\nlet cleanupHandlersRegistered = false;\nlet cleanupComplete = false;\n\n// Register process exit handlers to clean up temp files\nfunction registerCleanupHandlers() {\n if (cleanupHandlersRegistered) return;\n cleanupHandlersRegistered = true;\n\n const cleanup = () => {\n // Prevent duplicate cleanup if already run\n if (cleanupComplete) return;\n cleanupComplete = true;\n\n debugPage(`Cleaning up ${globalTempFiles.size} temporary dump files`);\n\n // Convert Set to array to avoid iteration issues while deleting\n const filesToClean = Array.from(globalTempFiles);\n for (const filePath of filesToClean) {\n try {\n rmSync(filePath, { force: true });\n } catch (error) {\n // Silently ignore errors during cleanup\n debugPage(`Failed to clean up temp file: ${filePath}`, error);\n }\n }\n\n // Clear the Set after all files are processed\n globalTempFiles.clear();\n };\n\n // Register cleanup on process exit\n process.once('SIGINT', cleanup);\n process.once('SIGTERM', cleanup);\n process.once('SIGHUP', cleanup);\n process.once('exit', cleanup);\n process.once('beforeExit', cleanup);\n\n // Handle uncaught exceptions and unhandled rejections\n process.once('uncaughtException', (error) => {\n debugPage('Uncaught exception detected, cleaning up temp files', error);\n cleanup();\n // Don't re-throw - let the process handle it normally\n });\n\n process.once('unhandledRejection', (reason) => {\n debugPage('Unhandled rejection detected, cleaning up temp files', reason);\n cleanup();\n // Don't re-throw - let the process handle it normally\n });\n}\n\ntype PlaywrightCacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id?: string;\n};\ntype PlaywrightCache = false | true | PlaywrightCacheConfig;\n\nexport const PlaywrightAiFixture = (options?: {\n forceSameTabNavigation?: boolean;\n waitForNetworkIdleTimeout?: number;\n waitForNavigationTimeout?: number;\n cache?: PlaywrightCache;\n}) => {\n const {\n forceSameTabNavigation = true,\n waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n cache,\n } = options ?? {};\n\n // Helper function to process cache configuration and auto-generate ID from test info\n const processTestCacheConfig = (testInfo: TestInfo): Cache | undefined => {\n // Generate ID from test info\n const { id } = groupAndCaseForTest(testInfo);\n\n // Use shared processCacheConfig with generated ID as fallback\n return processCacheConfig(cache as Cache, id);\n };\n\n // Track temporary dump files for each page\n const pageTempFiles = new Map<string, string>(); // pageId -> tempFilePath\n\n // Register global cleanup handlers\n registerCleanupHandlers();\n\n const pageAgentMap: Record<string, PageAgent<PlaywrightWebPage>> = {};\n const createOrReuseAgentForPage = (\n page: OriginPlaywrightPage,\n testInfo: TestInfo, // { testId: string; taskFile: string; taskTitle: string },\n opts?: WebPageAgentOpt,\n ) => {\n let idForPage = (page as any)[midsceneAgentKeyId];\n if (!idForPage) {\n idForPage = uuid();\n (page as any)[midsceneAgentKeyId] = idForPage;\n const { testId } = testInfo;\n const { file, title } = groupAndCaseForTest(testInfo);\n const cacheConfig = processTestCacheConfig(testInfo);\n\n pageAgentMap[idForPage] = new PlaywrightAgent(page, {\n testId: `playwright-${testId}-${idForPage}`,\n forceSameTabNavigation,\n cache: cacheConfig,\n groupName: title,\n groupDescription: file,\n generateReport: false, // we will generate it in the reporter\n ...opts,\n });\n\n pageAgentMap[idForPage].onDumpUpdate = (dump: string) => {\n updateDumpAnnotation(testInfo, dump, idForPage);\n };\n\n page.on('close', () => {\n debugPage('page closed');\n\n // Note: We don't clean up temp files here because the reporter\n // needs to read them in onTestEnd. The reporter will clean them up\n // after reading. If the test is interrupted (Ctrl+C), the process\n // exit handlers will clean up remaining temp files.\n\n // However, we do clean up the pageTempFiles Map entry to avoid memory leaks\n pageTempFiles.delete(idForPage);\n\n pageAgentMap[idForPage].destroy();\n delete pageAgentMap[idForPage];\n });\n }\n\n return pageAgentMap[idForPage];\n };\n\n async function generateAiFunction(options: {\n page: OriginPlaywrightPage;\n testInfo: TestInfo;\n use: any;\n aiActionType:\n | 'ai'\n | 'aiAct'\n | 'aiHover'\n | 'aiInput'\n | 'aiKeyboardPress'\n | 'aiScroll'\n | 'aiTap'\n | 'aiRightClick'\n | 'aiDoubleClick'\n | 'aiQuery'\n | 'aiAssert'\n | 'aiWaitFor'\n | 'aiLocate'\n | 'aiNumber'\n | 'aiString'\n | 'aiBoolean'\n | 'aiAsk'\n | 'runYaml'\n | 'setAIActionContext'\n | 'evaluateJavaScript'\n | 'recordToReport'\n | 'logScreenshot'\n | 'freezePageContext'\n | 'unfreezePageContext';\n }) {\n const { page, testInfo, use, aiActionType } = options;\n const agent = createOrReuseAgentForPage(page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n }) as PlaywrightAgent;\n\n await use(async (taskPrompt: string, ...args: any[]) => {\n return new Promise((resolve, reject) => {\n test.step(`ai-${aiActionType} - ${JSON.stringify(taskPrompt)}`, async () => {\n try {\n debugPage(\n `waitForNetworkIdle timeout: ${waitForNetworkIdleTimeout}`,\n );\n await agent.waitForNetworkIdle(waitForNetworkIdleTimeout);\n } catch (error) {\n console.warn(\n '[midscene:warning] Waiting for network idle has timed out, but Midscene will continue execution. Please check https://midscenejs.com/faq.html#customize-the-network-timeout for more information on customizing the network timeout',\n );\n }\n try {\n type AgentMethod = (\n prompt: string,\n ...restArgs: any[]\n ) => Promise<any>;\n const result = await (agent[aiActionType] as AgentMethod)(\n taskPrompt,\n ...(args || []),\n );\n resolve(result);\n } catch (error) {\n reject(error);\n }\n });\n });\n });\n }\n\n const updateDumpAnnotation = (\n test: TestInfo,\n dump: string,\n pageId: string,\n ) => {\n // 1. First, clean up the old temp file if it exists\n const oldTempFilePath = pageTempFiles.get(pageId);\n if (oldTempFilePath) {\n // Remove old temp file from tracking and try to delete it\n globalTempFiles.delete(oldTempFilePath);\n try {\n rmSync(oldTempFilePath, { force: true });\n } catch (error) {\n // Silently ignore if old file is already cleaned up\n }\n }\n\n // 2. Create new temp file with predictable name using pageId\n const tempFileName = `midscene-dump-${test.testId || uuid()}-${pageId}.json`;\n const tempFilePath = join(tmpdir(), tempFileName);\n\n // 3. Write dump to the new temporary file\n try {\n writeFileSync(tempFilePath, dump, 'utf-8');\n debugPage(`Dump written to temp file: ${tempFilePath}`);\n\n // 4. Track the new temp file (only if write succeeded)\n pageTempFiles.set(pageId, tempFilePath);\n globalTempFiles.add(tempFilePath);\n\n // Store only the file path in annotation (only if write succeeded)\n const currentAnnotation = test.annotations.find((item) => {\n return item.type === midsceneDumpAnnotationId;\n });\n if (currentAnnotation) {\n // Store file path instead of dump content\n currentAnnotation.description = tempFilePath;\n } else {\n test.annotations.push({\n type: midsceneDumpAnnotationId,\n description: tempFilePath,\n });\n }\n } catch (error) {\n // If write fails (e.g., disk full), don't track the file or add annotation\n // This prevents reporter from trying to read a non-existent file\n debugPage(\n `Failed to write temp file: ${tempFilePath}. Skipping annotation.`,\n error,\n );\n }\n };\n\n return {\n agentForPage: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await use(\n async (\n propsPage?: OriginPlaywrightPage | undefined,\n opts?: AgentOpt,\n ) => {\n const cacheConfig = processTestCacheConfig(testInfo);\n\n // Handle cache configuration priority:\n // 1. If user provides cache in opts, use it (but auto-generate ID if missing)\n // 2. Otherwise use fixture's cache config\n let finalCacheConfig = cacheConfig;\n if (opts?.cache !== undefined) {\n const userCache = opts.cache;\n if (userCache === false) {\n finalCacheConfig = false;\n } else if (userCache === true) {\n // Auto-generate ID for user's cache: true\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { id };\n } else if (typeof userCache === 'object') {\n if (!userCache.id) {\n // Auto-generate ID for user's cache object without ID\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { ...userCache, id };\n } else {\n finalCacheConfig = userCache;\n }\n }\n }\n\n const agent = createOrReuseAgentForPage(propsPage || page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n cache: finalCacheConfig,\n ...opts,\n });\n return agent;\n },\n );\n },\n ai: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'ai',\n });\n },\n aiAct: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAct',\n });\n },\n aiTap: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiTap',\n });\n },\n aiRightClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiRightClick',\n });\n },\n aiDoubleClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiDoubleClick',\n });\n },\n aiHover: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiHover',\n });\n },\n aiInput: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiInput',\n });\n },\n aiKeyboardPress: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiKeyboardPress',\n });\n },\n aiScroll: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiScroll',\n });\n },\n aiQuery: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiQuery',\n });\n },\n aiAssert: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAssert',\n });\n },\n aiWaitFor: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiWaitFor',\n });\n },\n aiLocate: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiLocate',\n });\n },\n aiNumber: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiNumber',\n });\n },\n aiString: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiString',\n });\n },\n aiBoolean: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiBoolean',\n });\n },\n aiAsk: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAsk',\n });\n },\n runYaml: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'runYaml',\n });\n },\n setAIActionContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'setAIActionContext',\n });\n },\n evaluateJavaScript: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'evaluateJavaScript',\n });\n },\n recordToReport: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'recordToReport',\n });\n },\n logScreenshot: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'logScreenshot',\n });\n },\n freezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'freezePageContext',\n });\n },\n unfreezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'unfreezePageContext',\n });\n },\n };\n};\n\nexport type PlayWrightAiFixtureType = {\n agentForPage: (\n page?: any,\n opts?: any,\n ) => Promise<PageAgent<PlaywrightWebPage>>;\n ai: <T = any>(prompt: string) => Promise<T>;\n aiAct: (taskPrompt: string) => ReturnType<PageAgent['aiAct']>;\n aiTap: (\n ...args: Parameters<PageAgent['aiTap']>\n ) => ReturnType<PageAgent['aiTap']>;\n aiRightClick: (\n ...args: Parameters<PageAgent['aiRightClick']>\n ) => ReturnType<PageAgent['aiRightClick']>;\n aiDoubleClick: (\n ...args: Parameters<PageAgent['aiDoubleClick']>\n ) => ReturnType<PageAgent['aiDoubleClick']>;\n aiHover: (\n ...args: Parameters<PageAgent['aiHover']>\n ) => ReturnType<PageAgent['aiHover']>;\n aiInput: (\n ...args: Parameters<PageAgent['aiInput']>\n ) => ReturnType<PageAgent['aiInput']>;\n aiKeyboardPress: (\n ...args: Parameters<PageAgent['aiKeyboardPress']>\n ) => ReturnType<PageAgent['aiKeyboardPress']>;\n aiScroll: (\n ...args: Parameters<PageAgent['aiScroll']>\n ) => ReturnType<PageAgent['aiScroll']>;\n aiQuery: <T = any>(...args: Parameters<PageAgent['aiQuery']>) => Promise<T>;\n aiAssert: (\n ...args: Parameters<PageAgent['aiAssert']>\n ) => ReturnType<PageAgent['aiAssert']>;\n aiWaitFor: (...args: Parameters<PageAgent['aiWaitFor']>) => Promise<void>;\n aiLocate: (\n ...args: Parameters<PageAgent['aiLocate']>\n ) => ReturnType<PageAgent['aiLocate']>;\n aiNumber: (\n ...args: Parameters<PageAgent['aiNumber']>\n ) => ReturnType<PageAgent['aiNumber']>;\n aiString: (\n ...args: Parameters<PageAgent['aiString']>\n ) => ReturnType<PageAgent['aiString']>;\n aiBoolean: (\n ...args: Parameters<PageAgent['aiBoolean']>\n ) => ReturnType<PageAgent['aiBoolean']>;\n aiAsk: (\n ...args: Parameters<PageAgent['aiAsk']>\n ) => ReturnType<PageAgent['aiAsk']>;\n runYaml: (\n ...args: Parameters<PageAgent['runYaml']>\n ) => ReturnType<PageAgent['runYaml']>;\n setAIActionContext: (\n ...args: Parameters<PageAgent['setAIActionContext']>\n ) => ReturnType<PageAgent['setAIActionContext']>;\n evaluateJavaScript: (\n ...args: Parameters<PageAgent['evaluateJavaScript']>\n ) => ReturnType<PageAgent['evaluateJavaScript']>;\n recordToReport: (\n ...args: Parameters<PageAgent['recordToReport']>\n ) => ReturnType<PageAgent['recordToReport']>;\n logScreenshot: (\n ...args: Parameters<PageAgent['logScreenshot']>\n ) => ReturnType<PageAgent['logScreenshot']>;\n freezePageContext: (\n ...args: Parameters<PageAgent['freezePageContext']>\n ) => ReturnType<PageAgent['freezePageContext']>;\n unfreezePageContext: (\n ...args: Parameters<PageAgent['unfreezePageContext']>\n ) => ReturnType<PageAgent['unfreezePageContext']>;\n};\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","debugPage","getDebug","groupAndCaseForTest","testInfo","taskFile","taskTitle","titlePath","taskTitleWithRetry","replaceIllegalPathCharsAndSpace","midsceneAgentKeyId","midsceneDumpAnnotationId","globalTempFiles","Set","cleanupHandlersRegistered","cleanupComplete","registerCleanupHandlers","cleanup","filesToClean","Array","filePath","rmSync","error","process","reason","PlaywrightAiFixture","options","forceSameTabNavigation","waitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","waitForNavigationTimeout","DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT","cache","processTestCacheConfig","id","processCacheConfig","pageTempFiles","Map","pageAgentMap","createOrReuseAgentForPage","page","opts","idForPage","uuid","testId","file","title","cacheConfig","PlaywrightAgent","dump","updateDumpAnnotation","generateAiFunction","use","aiActionType","agent","taskPrompt","args","Promise","resolve","reject","test","JSON","console","result","pageId","oldTempFilePath","tempFileName","tempFilePath","join","tmpdir","writeFileSync","currentAnnotation","item","propsPage","finalCacheConfig","undefined","userCache"],"mappings":"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C,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,SAAS,OACP,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,UAAU,OACR,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,WAAW,OACT,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,UAAU,OACR,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,UAAU,OACR,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,UAAU,OACR,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,WAAW,OACT,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,OAAO,OACL,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,SAAS,OACP,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,oBAAoB,OAClB,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,oBAAoB,OAClB,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,gBAAgB,OACd,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,eAAe,OACb,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,mBAAmB,OACjB,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;QACA,qBAAqB,OACnB,EAAEZ,IAAI,EAAkC,EACxCY,KACAhD;YAEA,MAAM+C,mBAAmB;gBACvBX;gBACApC;gBACAgD;gBACA,cAAc;YAChB;QACF;IACF;AACF"}
|
|
1
|
+
{"version":3,"file":"playwright/ai-fixture.js","sources":["webpack://@midscene/web/webpack/runtime/define_property_getters","webpack://@midscene/web/webpack/runtime/has_own_property","webpack://@midscene/web/webpack/runtime/make_namespace_object","webpack://@midscene/web/./src/playwright/ai-fixture.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 { rmSync, writeFileSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { PlaywrightAgent, type PlaywrightWebPage } from '@/playwright/index';\nimport type { WebPageAgentOpt } from '@/web-element';\nimport type { Cache } from '@midscene/core';\nimport type { AgentOpt, Agent as PageAgent } from '@midscene/core/agent';\nimport { processCacheConfig } from '@midscene/core/utils';\nimport {\n DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n} from '@midscene/shared/constants';\nimport { getDebug } from '@midscene/shared/logger';\nimport { uuid } from '@midscene/shared/utils';\nimport { replaceIllegalPathCharsAndSpace } from '@midscene/shared/utils';\nimport { type TestInfo, type TestType, test } from '@playwright/test';\nimport type { Page as OriginPlaywrightPage } from 'playwright';\nexport type APITestType = Pick<TestType<any, any>, 'step'>;\n\nconst debugPage = getDebug('web:playwright:ai-fixture');\n\nconst groupAndCaseForTest = (testInfo: TestInfo) => {\n let taskFile: string;\n let taskTitle: string;\n const titlePath = [...testInfo.titlePath];\n\n if (titlePath.length > 1) {\n taskFile = titlePath.shift() || 'unnamed';\n taskTitle = titlePath.join('__');\n } else if (titlePath.length === 1) {\n taskTitle = titlePath[0];\n taskFile = `${taskTitle}`;\n } else {\n taskTitle = 'unnamed';\n taskFile = 'unnamed';\n }\n\n const taskTitleWithRetry = `${taskTitle}${testInfo.retry ? `(retry #${testInfo.retry})` : ''}`;\n\n return {\n file: taskFile,\n id: replaceIllegalPathCharsAndSpace(`${taskFile}(${taskTitle})`),\n title: replaceIllegalPathCharsAndSpace(taskTitleWithRetry),\n };\n};\n\nconst midsceneAgentKeyId = '_midsceneAgentId';\nexport const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';\n\n// Track temporary dump files per page for cleanup\nconst pageTempFiles = new Map<string, string>();\n\ntype PlaywrightCacheConfig = {\n strategy?: 'read-only' | 'read-write' | 'write-only';\n id?: string;\n};\ntype PlaywrightCache = false | true | PlaywrightCacheConfig;\n\nexport const PlaywrightAiFixture = (options?: {\n forceSameTabNavigation?: boolean;\n waitForNetworkIdleTimeout?: number;\n waitForNavigationTimeout?: number;\n cache?: PlaywrightCache;\n}) => {\n const {\n forceSameTabNavigation = true,\n waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT,\n waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT,\n cache,\n } = options ?? {};\n\n // Helper function to process cache configuration and auto-generate ID from test info\n const processTestCacheConfig = (testInfo: TestInfo): Cache | undefined => {\n // Generate ID from test info\n const { id } = groupAndCaseForTest(testInfo);\n\n // Use shared processCacheConfig with generated ID as fallback\n return processCacheConfig(cache as Cache, id);\n };\n\n const pageAgentMap: Record<string, PageAgent<PlaywrightWebPage>> = {};\n const createOrReuseAgentForPage = (\n page: OriginPlaywrightPage,\n testInfo: TestInfo, // { testId: string; taskFile: string; taskTitle: string },\n opts?: WebPageAgentOpt,\n ) => {\n let idForPage = (page as any)[midsceneAgentKeyId];\n if (!idForPage) {\n idForPage = uuid();\n (page as any)[midsceneAgentKeyId] = idForPage;\n const { testId } = testInfo;\n const { file, title } = groupAndCaseForTest(testInfo);\n const cacheConfig = processTestCacheConfig(testInfo);\n\n pageAgentMap[idForPage] = new PlaywrightAgent(page, {\n testId: `playwright-${testId}-${idForPage}`,\n forceSameTabNavigation,\n cache: cacheConfig,\n groupName: title,\n groupDescription: file,\n generateReport: false, // we will generate it in the reporter\n ...opts,\n });\n\n pageAgentMap[idForPage].onDumpUpdate = (dump: string) => {\n updateDumpAnnotation(testInfo, dump, idForPage);\n };\n\n page.on('close', () => {\n debugPage('page closed');\n\n // Note: We don't clean up temp files here because the reporter\n // needs to read them in onTestEnd. The reporter will clean them up\n // after reading. If the test is interrupted (Ctrl+C), the process\n // exit handlers will clean up remaining temp files.\n\n // However, we do clean up the pageTempFiles Map entry to avoid memory leaks\n pageTempFiles.delete(idForPage);\n\n pageAgentMap[idForPage].destroy();\n delete pageAgentMap[idForPage];\n });\n }\n\n return pageAgentMap[idForPage];\n };\n\n async function generateAiFunction(options: {\n page: OriginPlaywrightPage;\n testInfo: TestInfo;\n use: any;\n aiActionType:\n | 'ai'\n | 'aiAct'\n | 'aiHover'\n | 'aiInput'\n | 'aiKeyboardPress'\n | 'aiScroll'\n | 'aiTap'\n | 'aiRightClick'\n | 'aiDoubleClick'\n | 'aiQuery'\n | 'aiAssert'\n | 'aiWaitFor'\n | 'aiLocate'\n | 'aiNumber'\n | 'aiString'\n | 'aiBoolean'\n | 'aiAsk'\n | 'runYaml'\n | 'setAIActionContext'\n | 'evaluateJavaScript'\n | 'recordToReport'\n | 'logScreenshot'\n | 'freezePageContext'\n | 'unfreezePageContext';\n }) {\n const { page, testInfo, use, aiActionType } = options;\n const agent = createOrReuseAgentForPage(page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n }) as PlaywrightAgent;\n\n await use(async (taskPrompt: string, ...args: any[]) => {\n return new Promise((resolve, reject) => {\n test.step(`ai-${aiActionType} - ${JSON.stringify(taskPrompt)}`, async () => {\n try {\n debugPage(\n `waitForNetworkIdle timeout: ${waitForNetworkIdleTimeout}`,\n );\n await agent.waitForNetworkIdle(waitForNetworkIdleTimeout);\n } catch (error) {\n console.warn(\n '[midscene:warning] Waiting for network idle has timed out, but Midscene will continue execution. Please check https://midscenejs.com/faq.html#customize-the-network-timeout for more information on customizing the network timeout',\n );\n }\n try {\n type AgentMethod = (\n prompt: string,\n ...restArgs: any[]\n ) => Promise<any>;\n const result = await (agent[aiActionType] as AgentMethod)(\n taskPrompt,\n ...(args || []),\n );\n resolve(result);\n } catch (error) {\n reject(error);\n }\n });\n });\n });\n }\n\n const updateDumpAnnotation = (\n test: TestInfo,\n dump: string,\n pageId: string,\n ) => {\n // 1. First, clean up the old temp file if it exists\n const oldTempFilePath = pageTempFiles.get(pageId);\n if (oldTempFilePath) {\n try {\n rmSync(oldTempFilePath, { force: true });\n } catch (error) {\n // Silently ignore if old file is already cleaned up\n }\n }\n\n // 2. Create new temp file with predictable name using pageId\n const tempFileName = `midscene-dump-${test.testId || uuid()}-${pageId}.json`;\n const tempFilePath = join(tmpdir(), tempFileName);\n\n // 3. Write dump to the new temporary file\n try {\n writeFileSync(tempFilePath, dump, 'utf-8');\n debugPage(`Dump written to temp file: ${tempFilePath}`);\n\n // 4. Track the new temp file (only if write succeeded)\n pageTempFiles.set(pageId, tempFilePath);\n\n // Store only the file path in annotation (only if write succeeded)\n const currentAnnotation = test.annotations.find((item) => {\n return item.type === midsceneDumpAnnotationId;\n });\n if (currentAnnotation) {\n // Store file path instead of dump content\n currentAnnotation.description = tempFilePath;\n } else {\n test.annotations.push({\n type: midsceneDumpAnnotationId,\n description: tempFilePath,\n });\n }\n } catch (error) {\n // If write fails (e.g., disk full), don't track the file or add annotation\n // This prevents reporter from trying to read a non-existent file\n debugPage(\n `Failed to write temp file: ${tempFilePath}. Skipping annotation.`,\n error,\n );\n }\n };\n\n return {\n agentForPage: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await use(\n async (\n propsPage?: OriginPlaywrightPage | undefined,\n opts?: AgentOpt,\n ) => {\n const cacheConfig = processTestCacheConfig(testInfo);\n\n // Handle cache configuration priority:\n // 1. If user provides cache in opts, use it (but auto-generate ID if missing)\n // 2. Otherwise use fixture's cache config\n let finalCacheConfig = cacheConfig;\n if (opts?.cache !== undefined) {\n const userCache = opts.cache;\n if (userCache === false) {\n finalCacheConfig = false;\n } else if (userCache === true) {\n // Auto-generate ID for user's cache: true\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { id };\n } else if (typeof userCache === 'object') {\n if (!userCache.id) {\n // Auto-generate ID for user's cache object without ID\n const { id } = groupAndCaseForTest(testInfo);\n finalCacheConfig = { ...userCache, id };\n } else {\n finalCacheConfig = userCache;\n }\n }\n }\n\n const agent = createOrReuseAgentForPage(propsPage || page, testInfo, {\n waitForNavigationTimeout,\n waitForNetworkIdleTimeout,\n cache: finalCacheConfig,\n ...opts,\n });\n return agent;\n },\n );\n },\n ai: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'ai',\n });\n },\n aiAct: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAct',\n });\n },\n aiTap: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiTap',\n });\n },\n aiRightClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiRightClick',\n });\n },\n aiDoubleClick: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiDoubleClick',\n });\n },\n aiHover: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiHover',\n });\n },\n aiInput: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiInput',\n });\n },\n aiKeyboardPress: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiKeyboardPress',\n });\n },\n aiScroll: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiScroll',\n });\n },\n aiQuery: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiQuery',\n });\n },\n aiAssert: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAssert',\n });\n },\n aiWaitFor: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiWaitFor',\n });\n },\n aiLocate: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiLocate',\n });\n },\n aiNumber: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiNumber',\n });\n },\n aiString: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiString',\n });\n },\n aiBoolean: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiBoolean',\n });\n },\n aiAsk: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'aiAsk',\n });\n },\n runYaml: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'runYaml',\n });\n },\n setAIActionContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'setAIActionContext',\n });\n },\n evaluateJavaScript: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'evaluateJavaScript',\n });\n },\n recordToReport: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'recordToReport',\n });\n },\n logScreenshot: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'logScreenshot',\n });\n },\n freezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'freezePageContext',\n });\n },\n unfreezePageContext: async (\n { page }: { page: OriginPlaywrightPage },\n use: any,\n testInfo: TestInfo,\n ) => {\n await generateAiFunction({\n page,\n testInfo,\n use,\n aiActionType: 'unfreezePageContext',\n });\n },\n };\n};\n\nexport type PlayWrightAiFixtureType = {\n agentForPage: (\n page?: any,\n opts?: any,\n ) => Promise<PageAgent<PlaywrightWebPage>>;\n ai: <T = any>(prompt: string) => Promise<T>;\n aiAct: (taskPrompt: string) => ReturnType<PageAgent['aiAct']>;\n aiTap: (\n ...args: Parameters<PageAgent['aiTap']>\n ) => ReturnType<PageAgent['aiTap']>;\n aiRightClick: (\n ...args: Parameters<PageAgent['aiRightClick']>\n ) => ReturnType<PageAgent['aiRightClick']>;\n aiDoubleClick: (\n ...args: Parameters<PageAgent['aiDoubleClick']>\n ) => ReturnType<PageAgent['aiDoubleClick']>;\n aiHover: (\n ...args: Parameters<PageAgent['aiHover']>\n ) => ReturnType<PageAgent['aiHover']>;\n aiInput: (\n ...args: Parameters<PageAgent['aiInput']>\n ) => ReturnType<PageAgent['aiInput']>;\n aiKeyboardPress: (\n ...args: Parameters<PageAgent['aiKeyboardPress']>\n ) => ReturnType<PageAgent['aiKeyboardPress']>;\n aiScroll: (\n ...args: Parameters<PageAgent['aiScroll']>\n ) => ReturnType<PageAgent['aiScroll']>;\n aiQuery: <T = any>(...args: Parameters<PageAgent['aiQuery']>) => Promise<T>;\n aiAssert: (\n ...args: Parameters<PageAgent['aiAssert']>\n ) => ReturnType<PageAgent['aiAssert']>;\n aiWaitFor: (...args: Parameters<PageAgent['aiWaitFor']>) => Promise<void>;\n aiLocate: (\n ...args: Parameters<PageAgent['aiLocate']>\n ) => ReturnType<PageAgent['aiLocate']>;\n aiNumber: (\n ...args: Parameters<PageAgent['aiNumber']>\n ) => ReturnType<PageAgent['aiNumber']>;\n aiString: (\n ...args: Parameters<PageAgent['aiString']>\n ) => ReturnType<PageAgent['aiString']>;\n aiBoolean: (\n ...args: Parameters<PageAgent['aiBoolean']>\n ) => ReturnType<PageAgent['aiBoolean']>;\n aiAsk: (\n ...args: Parameters<PageAgent['aiAsk']>\n ) => ReturnType<PageAgent['aiAsk']>;\n runYaml: (\n ...args: Parameters<PageAgent['runYaml']>\n ) => ReturnType<PageAgent['runYaml']>;\n setAIActionContext: (\n ...args: Parameters<PageAgent['setAIActionContext']>\n ) => ReturnType<PageAgent['setAIActionContext']>;\n evaluateJavaScript: (\n ...args: Parameters<PageAgent['evaluateJavaScript']>\n ) => ReturnType<PageAgent['evaluateJavaScript']>;\n recordToReport: (\n ...args: Parameters<PageAgent['recordToReport']>\n ) => ReturnType<PageAgent['recordToReport']>;\n logScreenshot: (\n ...args: Parameters<PageAgent['logScreenshot']>\n ) => ReturnType<PageAgent['logScreenshot']>;\n freezePageContext: (\n ...args: Parameters<PageAgent['freezePageContext']>\n ) => ReturnType<PageAgent['freezePageContext']>;\n unfreezePageContext: (\n ...args: Parameters<PageAgent['unfreezePageContext']>\n ) => ReturnType<PageAgent['unfreezePageContext']>;\n};\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","debugPage","getDebug","groupAndCaseForTest","testInfo","taskFile","taskTitle","titlePath","taskTitleWithRetry","replaceIllegalPathCharsAndSpace","midsceneAgentKeyId","midsceneDumpAnnotationId","pageTempFiles","Map","PlaywrightAiFixture","options","forceSameTabNavigation","waitForNetworkIdleTimeout","DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT","waitForNavigationTimeout","DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT","cache","processTestCacheConfig","id","processCacheConfig","pageAgentMap","createOrReuseAgentForPage","page","opts","idForPage","uuid","testId","file","title","cacheConfig","PlaywrightAgent","dump","updateDumpAnnotation","generateAiFunction","use","aiActionType","agent","taskPrompt","args","Promise","resolve","reject","test","JSON","error","console","result","pageId","oldTempFilePath","rmSync","tempFileName","tempFilePath","join","tmpdir","writeFileSync","currentAnnotation","item","propsPage","finalCacheConfig","undefined","userCache"],"mappings":"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"}
|
|
@@ -27,8 +27,6 @@ __webpack_require__.d(__webpack_exports__, {
|
|
|
27
27
|
default: ()=>reporter
|
|
28
28
|
});
|
|
29
29
|
const external_node_fs_namespaceObject = require("node:fs");
|
|
30
|
-
const external_node_os_namespaceObject = require("node:os");
|
|
31
|
-
const external_node_path_namespaceObject = require("node:path");
|
|
32
30
|
const agent_namespaceObject = require("@midscene/core/agent");
|
|
33
31
|
const utils_namespaceObject = require("@midscene/core/utils");
|
|
34
32
|
const shared_utils_namespaceObject = require("@midscene/shared/utils");
|
|
@@ -80,6 +78,7 @@ class MidsceneReporter {
|
|
|
80
78
|
const dumpAnnotation = test.annotations.find((annotation)=>'MIDSCENE_DUMP_ANNOTATION' === annotation.type);
|
|
81
79
|
if (!(null == dumpAnnotation ? void 0 : dumpAnnotation.description)) return;
|
|
82
80
|
const tempFilePath = dumpAnnotation.description;
|
|
81
|
+
this.tempFiles.add(tempFilePath);
|
|
83
82
|
let dumpString;
|
|
84
83
|
try {
|
|
85
84
|
dumpString = (0, external_node_fs_namespaceObject.readFileSync)(tempFilePath, 'utf-8');
|
|
@@ -104,38 +103,27 @@ class MidsceneReporter {
|
|
|
104
103
|
(0, external_node_fs_namespaceObject.rmSync)(tempFilePath, {
|
|
105
104
|
force: true
|
|
106
105
|
});
|
|
106
|
+
this.tempFiles.delete(tempFilePath);
|
|
107
107
|
} catch (error) {
|
|
108
108
|
console.warn(`Failed to delete Midscene temp file: ${tempFilePath}`, error);
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
console.log(`Midscene: Found ${orphanedFiles.length} orphaned temp file(s), cleaning up...`);
|
|
121
|
-
for (const file of orphanedFiles){
|
|
122
|
-
const filePath = (0, external_node_path_namespaceObject.join)(tmpDir, file);
|
|
123
|
-
try {
|
|
124
|
-
(0, external_node_fs_namespaceObject.rmSync)(filePath, {
|
|
125
|
-
force: true
|
|
126
|
-
});
|
|
127
|
-
console.log(`Midscene: Cleaned up orphaned temp file: ${file}`);
|
|
128
|
-
} catch (error) {}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
} catch (error) {
|
|
132
|
-
console.warn('Midscene: Failed to scan for orphaned temp files:', error);
|
|
111
|
+
onEnd() {
|
|
112
|
+
if (this.tempFiles.size > 0) {
|
|
113
|
+
console.log(`Midscene: Cleaning up ${this.tempFiles.size} remaining temp file(s)...`);
|
|
114
|
+
for (const filePath of this.tempFiles)try {
|
|
115
|
+
(0, external_node_fs_namespaceObject.rmSync)(filePath, {
|
|
116
|
+
force: true
|
|
117
|
+
});
|
|
118
|
+
} catch (error) {}
|
|
119
|
+
this.tempFiles.clear();
|
|
133
120
|
}
|
|
134
121
|
}
|
|
135
122
|
constructor(options = {}){
|
|
136
123
|
_define_property(this, "mergedFilename", void 0);
|
|
137
124
|
_define_property(this, "testTitleToFilename", new Map());
|
|
138
125
|
_define_property(this, "mode", void 0);
|
|
126
|
+
_define_property(this, "tempFiles", new Set());
|
|
139
127
|
this.mode = MidsceneReporter.getMode(options.type ?? 'merged');
|
|
140
128
|
}
|
|
141
129
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright/reporter/index.js","sources":["webpack://@midscene/web/webpack/runtime/define_property_getters","webpack://@midscene/web/webpack/runtime/has_own_property","webpack://@midscene/web/webpack/runtime/make_namespace_object","webpack://@midscene/web/./src/playwright/reporter/index.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 { readFileSync, readdirSync, rmSync } from 'node:fs';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport type { ReportDumpWithAttributes } from '@midscene/core';\nimport { getReportFileName, printReportMsg } from '@midscene/core/agent';\nimport { writeDumpReport } from '@midscene/core/utils';\nimport { replaceIllegalPathCharsAndSpace } from '@midscene/shared/utils';\nimport type {\n FullConfig,\n FullResult,\n Reporter,\n Suite,\n TestCase,\n TestError,\n TestResult,\n} from '@playwright/test/reporter';\n\ninterface MidsceneReporterOptions {\n type?: 'merged' | 'separate';\n}\n\nclass MidsceneReporter implements Reporter {\n private mergedFilename?: string;\n private testTitleToFilename = new Map<string, string>();\n mode?: 'merged' | 'separate';\n\n constructor(options: MidsceneReporterOptions = {}) {\n // Set mode from constructor options (official Playwright way)\n this.mode = MidsceneReporter.getMode(options.type ?? 'merged');\n }\n\n private static getMode(reporterType: string): 'merged' | 'separate' {\n if (!reporterType) {\n return 'merged';\n }\n if (reporterType !== 'merged' && reporterType !== 'separate') {\n throw new Error(\n `Unknown reporter type in playwright config: ${reporterType}, only support 'merged' or 'separate'`,\n );\n }\n return reporterType;\n }\n\n private getSeparatedFilename(testTitle: string): string {\n if (!this.testTitleToFilename.has(testTitle)) {\n const baseTag = `playwright-${replaceIllegalPathCharsAndSpace(testTitle)}`;\n const generatedFilename = getReportFileName(baseTag);\n this.testTitleToFilename.set(testTitle, generatedFilename);\n }\n return this.testTitleToFilename.get(testTitle)!;\n }\n\n private getReportFilename(testTitle?: string): string {\n if (this.mode === 'merged') {\n if (!this.mergedFilename) {\n this.mergedFilename = getReportFileName('playwright-merged');\n }\n return this.mergedFilename;\n } else if (this.mode === 'separate') {\n if (!testTitle) throw new Error('testTitle is required in separate mode');\n return this.getSeparatedFilename(testTitle);\n }\n throw new Error(`Unknown mode: ${this.mode}`);\n }\n\n private updateReport(testData: ReportDumpWithAttributes) {\n if (!testData || !this.mode) return;\n const fileName = this.getReportFilename(\n testData.attributes?.playwright_test_title,\n );\n const reportPath = writeDumpReport(\n fileName,\n testData,\n this.mode === 'merged',\n );\n reportPath && printReportMsg(reportPath);\n }\n\n async onBegin(config: FullConfig, suite: Suite) {}\n\n onTestBegin(_test: TestCase, _result: TestResult) {\n // logger(`Starting test ${test.title}`);\n }\n\n onTestEnd(test: TestCase, result: TestResult) {\n const dumpAnnotation = test.annotations.find((annotation) => {\n return annotation.type === 'MIDSCENE_DUMP_ANNOTATION';\n });\n if (!dumpAnnotation?.description) return;\n\n const tempFilePath = dumpAnnotation.description;\n let dumpString: string | undefined;\n\n try {\n dumpString = readFileSync(tempFilePath, 'utf-8');\n } catch (error) {\n console.error(\n `Failed to read Midscene dump file: ${tempFilePath}`,\n error,\n );\n // Don't return here - we still need to clean up the temp file\n }\n\n // Only update report if we successfully read the dump\n if (dumpString) {\n const retry = result.retry ? `(retry #${result.retry})` : '';\n const testId = `${test.id}${retry}`;\n const testData: ReportDumpWithAttributes = {\n dumpString,\n attributes: {\n playwright_test_id: testId,\n playwright_test_title: `${test.title}${retry}`,\n playwright_test_status: result.status,\n playwright_test_duration: result.duration,\n },\n };\n\n this.updateReport(testData);\n }\n\n // Always clean up temp file, even if reading failed\n try {\n rmSync(tempFilePath, { force: true });\n } catch (error) {\n console.warn(\n `Failed to delete Midscene temp file: ${tempFilePath}`,\n error,\n );\n }\n }\n\n onError(error: TestError) {\n // Reporter-level errors might prevent onTestEnd from being called\n // Log the error but don't attempt cleanup here since we don't have\n // access to specific temp files. The onEnd hook will handle orphaned files.\n console.error('Midscene Reporter error occurred:', error);\n }\n\n onEnd(result: FullResult) {\n // Final cleanup: scan for any orphaned temp files that may have been\n // left behind by crashed workers or reporter errors\n try {\n const tmpDir = tmpdir();\n const files = readdirSync(tmpDir);\n const orphanedFiles = files.filter((f) =>\n f.startsWith('midscene-dump-'),\n );\n\n if (orphanedFiles.length > 0) {\n console.log(\n `Midscene: Found ${orphanedFiles.length} orphaned temp file(s), cleaning up...`,\n );\n\n for (const file of orphanedFiles) {\n const filePath = join(tmpDir, file);\n try {\n rmSync(filePath, { force: true });\n console.log(`Midscene: Cleaned up orphaned temp file: ${file}`);\n } catch (error) {\n // Silently ignore individual file cleanup errors\n }\n }\n }\n } catch (error) {\n // Silently ignore directory read errors\n console.warn('Midscene: Failed to scan for orphaned temp files:', error);\n }\n }\n}\n\nexport default MidsceneReporter;\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","MidsceneReporter","reporterType","Error","testTitle","baseTag","replaceIllegalPathCharsAndSpace","generatedFilename","getReportFileName","testData","_testData_attributes","fileName","reportPath","writeDumpReport","printReportMsg","config","suite","_test","_result","test","result","dumpAnnotation","annotation","tempFilePath","dumpString","readFileSync","error","console","retry","testId","rmSync","tmpDir","tmpdir","files","readdirSync","orphanedFiles","f","file","filePath","join","options","Map"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"playwright/reporter/index.js","sources":["webpack://@midscene/web/webpack/runtime/define_property_getters","webpack://@midscene/web/webpack/runtime/has_own_property","webpack://@midscene/web/webpack/runtime/make_namespace_object","webpack://@midscene/web/./src/playwright/reporter/index.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 { readFileSync, rmSync } from 'node:fs';\nimport type { ReportDumpWithAttributes } from '@midscene/core';\nimport { getReportFileName, printReportMsg } from '@midscene/core/agent';\nimport { writeDumpReport } from '@midscene/core/utils';\nimport { replaceIllegalPathCharsAndSpace } from '@midscene/shared/utils';\nimport type {\n FullConfig,\n Reporter,\n Suite,\n TestCase,\n TestResult,\n} from '@playwright/test/reporter';\n\ninterface MidsceneReporterOptions {\n type?: 'merged' | 'separate';\n}\n\nclass MidsceneReporter implements Reporter {\n private mergedFilename?: string;\n private testTitleToFilename = new Map<string, string>();\n mode?: 'merged' | 'separate';\n\n // Track all temp files created during this test run for cleanup\n private tempFiles = new Set<string>();\n\n constructor(options: MidsceneReporterOptions = {}) {\n // Set mode from constructor options (official Playwright way)\n this.mode = MidsceneReporter.getMode(options.type ?? 'merged');\n }\n\n private static getMode(reporterType: string): 'merged' | 'separate' {\n if (!reporterType) {\n return 'merged';\n }\n if (reporterType !== 'merged' && reporterType !== 'separate') {\n throw new Error(\n `Unknown reporter type in playwright config: ${reporterType}, only support 'merged' or 'separate'`,\n );\n }\n return reporterType;\n }\n\n private getSeparatedFilename(testTitle: string): string {\n if (!this.testTitleToFilename.has(testTitle)) {\n const baseTag = `playwright-${replaceIllegalPathCharsAndSpace(testTitle)}`;\n const generatedFilename = getReportFileName(baseTag);\n this.testTitleToFilename.set(testTitle, generatedFilename);\n }\n return this.testTitleToFilename.get(testTitle)!;\n }\n\n private getReportFilename(testTitle?: string): string {\n if (this.mode === 'merged') {\n if (!this.mergedFilename) {\n this.mergedFilename = getReportFileName('playwright-merged');\n }\n return this.mergedFilename;\n } else if (this.mode === 'separate') {\n if (!testTitle) throw new Error('testTitle is required in separate mode');\n return this.getSeparatedFilename(testTitle);\n }\n throw new Error(`Unknown mode: ${this.mode}`);\n }\n\n private updateReport(testData: ReportDumpWithAttributes) {\n if (!testData || !this.mode) return;\n const fileName = this.getReportFilename(\n testData.attributes?.playwright_test_title,\n );\n const reportPath = writeDumpReport(\n fileName,\n testData,\n this.mode === 'merged',\n );\n reportPath && printReportMsg(reportPath);\n }\n\n async onBegin(config: FullConfig, suite: Suite) {}\n\n onTestBegin(_test: TestCase, _result: TestResult) {\n // logger(`Starting test ${test.title}`);\n }\n\n onTestEnd(test: TestCase, result: TestResult) {\n const dumpAnnotation = test.annotations.find((annotation) => {\n return annotation.type === 'MIDSCENE_DUMP_ANNOTATION';\n });\n if (!dumpAnnotation?.description) return;\n\n const tempFilePath = dumpAnnotation.description;\n\n // Track this temp file for potential cleanup in onEnd\n this.tempFiles.add(tempFilePath);\n\n let dumpString: string | undefined;\n\n try {\n dumpString = readFileSync(tempFilePath, 'utf-8');\n } catch (error) {\n console.error(\n `Failed to read Midscene dump file: ${tempFilePath}`,\n error,\n );\n // Don't return here - we still need to clean up the temp file\n }\n\n // Only update report if we successfully read the dump\n if (dumpString) {\n const retry = result.retry ? `(retry #${result.retry})` : '';\n const testId = `${test.id}${retry}`;\n const testData: ReportDumpWithAttributes = {\n dumpString,\n attributes: {\n playwright_test_id: testId,\n playwright_test_title: `${test.title}${retry}`,\n playwright_test_status: result.status,\n playwright_test_duration: result.duration,\n },\n };\n\n this.updateReport(testData);\n }\n\n // Always try to clean up temp file\n try {\n rmSync(tempFilePath, { force: true });\n // If successfully deleted, remove from tracking\n this.tempFiles.delete(tempFilePath);\n } catch (error) {\n console.warn(\n `Failed to delete Midscene temp file: ${tempFilePath}`,\n error,\n );\n // Keep in tempFiles for cleanup in onEnd\n }\n }\n\n onEnd() {\n // Clean up any remaining temp files that weren't deleted in onTestEnd\n if (this.tempFiles.size > 0) {\n console.log(\n `Midscene: Cleaning up ${this.tempFiles.size} remaining temp file(s)...`,\n );\n\n for (const filePath of this.tempFiles) {\n try {\n rmSync(filePath, { force: true });\n } catch (error) {\n // Silently ignore - file may have been deleted already\n }\n }\n\n this.tempFiles.clear();\n }\n }\n}\n\nexport default MidsceneReporter;\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","MidsceneReporter","reporterType","Error","testTitle","baseTag","replaceIllegalPathCharsAndSpace","generatedFilename","getReportFileName","testData","_testData_attributes","fileName","reportPath","writeDumpReport","printReportMsg","config","suite","_test","_result","test","result","dumpAnnotation","annotation","tempFilePath","dumpString","readFileSync","error","console","retry","testId","rmSync","filePath","options","Map","Set"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FullConfig,
|
|
1
|
+
import type { FullConfig, Reporter, Suite, TestCase, TestResult } from '@playwright/test/reporter';
|
|
2
2
|
interface MidsceneReporterOptions {
|
|
3
3
|
type?: 'merged' | 'separate';
|
|
4
4
|
}
|
|
@@ -6,6 +6,7 @@ declare class MidsceneReporter implements Reporter {
|
|
|
6
6
|
private mergedFilename?;
|
|
7
7
|
private testTitleToFilename;
|
|
8
8
|
mode?: 'merged' | 'separate';
|
|
9
|
+
private tempFiles;
|
|
9
10
|
constructor(options?: MidsceneReporterOptions);
|
|
10
11
|
private static getMode;
|
|
11
12
|
private getSeparatedFilename;
|
|
@@ -14,7 +15,6 @@ declare class MidsceneReporter implements Reporter {
|
|
|
14
15
|
onBegin(config: FullConfig, suite: Suite): Promise<void>;
|
|
15
16
|
onTestBegin(_test: TestCase, _result: TestResult): void;
|
|
16
17
|
onTestEnd(test: TestCase, result: TestResult): void;
|
|
17
|
-
|
|
18
|
-
onEnd(result: FullResult): void;
|
|
18
|
+
onEnd(): void;
|
|
19
19
|
}
|
|
20
20
|
export default MidsceneReporter;
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"Browser use",
|
|
9
9
|
"Android use"
|
|
10
10
|
],
|
|
11
|
-
"version": "1.0.1-beta-
|
|
11
|
+
"version": "1.0.1-beta-20251028121806.0",
|
|
12
12
|
"repository": "https://github.com/web-infra-dev/midscene",
|
|
13
13
|
"homepage": "https://midscenejs.com/",
|
|
14
14
|
"main": "./dist/lib/index.js",
|
|
@@ -103,9 +103,9 @@
|
|
|
103
103
|
"http-server": "14.1.1",
|
|
104
104
|
"socket.io": "^4.8.1",
|
|
105
105
|
"socket.io-client": "4.8.1",
|
|
106
|
-
"@midscene/core": "1.0.1-beta-
|
|
107
|
-
"@midscene/shared": "1.0.1-beta-
|
|
108
|
-
"@midscene/playground": "1.0.1-beta-
|
|
106
|
+
"@midscene/core": "1.0.1-beta-20251028121806.0",
|
|
107
|
+
"@midscene/shared": "1.0.1-beta-20251028121806.0",
|
|
108
|
+
"@midscene/playground": "1.0.1-beta-20251028121806.0"
|
|
109
109
|
},
|
|
110
110
|
"devDependencies": {
|
|
111
111
|
"@playwright/test": "^1.44.1",
|