@midscene/web 1.0.1-beta-20251028021317.0 → 1.0.1-beta-20251028065320.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 +64 -12
- package/dist/es/playwright/ai-fixture.mjs.map +1 -1
- package/dist/es/playwright/reporter/index.mjs +41 -14
- 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 +63 -11
- package/dist/lib/playwright/ai-fixture.js.map +1 -1
- package/dist/lib/playwright/reporter/index.js +40 -13
- package/dist/lib/playwright/reporter/index.js.map +1 -1
- package/dist/types/playwright/reporter/index.d.ts +3 -1
- 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-20251028065320.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-20251028065320.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-20251028065320.0`, 'log');
|
|
50
50
|
}
|
|
51
51
|
async connect() {
|
|
52
52
|
return await this.setupBridgeClient();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { writeFileSync } from "node:fs";
|
|
1
|
+
import { rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { PlaywrightAgent } from "./index.mjs";
|
|
@@ -33,12 +33,48 @@ const groupAndCaseForTest = (testInfo)=>{
|
|
|
33
33
|
};
|
|
34
34
|
const midsceneAgentKeyId = '_midsceneAgentId';
|
|
35
35
|
const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';
|
|
36
|
+
const globalTempFiles = new Set();
|
|
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
70
|
const PlaywrightAiFixture = (options)=>{
|
|
37
71
|
const { forceSameTabNavigation = true, waitForNetworkIdleTimeout = DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT, waitForNavigationTimeout = DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, cache } = options ?? {};
|
|
38
72
|
const processTestCacheConfig = (testInfo)=>{
|
|
39
73
|
const { id } = groupAndCaseForTest(testInfo);
|
|
40
74
|
return processCacheConfig(cache, id);
|
|
41
75
|
};
|
|
76
|
+
const pageTempFiles = new Map();
|
|
77
|
+
registerCleanupHandlers();
|
|
42
78
|
const pageAgentMap = {};
|
|
43
79
|
const createOrReuseAgentForPage = (page, testInfo, opts)=>{
|
|
44
80
|
let idForPage = page[midsceneAgentKeyId];
|
|
@@ -58,10 +94,11 @@ const PlaywrightAiFixture = (options)=>{
|
|
|
58
94
|
...opts
|
|
59
95
|
});
|
|
60
96
|
pageAgentMap[idForPage].onDumpUpdate = (dump)=>{
|
|
61
|
-
updateDumpAnnotation(testInfo, dump);
|
|
97
|
+
updateDumpAnnotation(testInfo, dump, idForPage);
|
|
62
98
|
};
|
|
63
99
|
page.on('close', ()=>{
|
|
64
100
|
debugPage('page closed');
|
|
101
|
+
pageTempFiles.delete(idForPage);
|
|
65
102
|
pageAgentMap[idForPage].destroy();
|
|
66
103
|
delete pageAgentMap[idForPage];
|
|
67
104
|
});
|
|
@@ -91,17 +128,32 @@ const PlaywrightAiFixture = (options)=>{
|
|
|
91
128
|
});
|
|
92
129
|
}));
|
|
93
130
|
}
|
|
94
|
-
const updateDumpAnnotation = (test, dump)=>{
|
|
95
|
-
const
|
|
131
|
+
const updateDumpAnnotation = (test, dump, pageId)=>{
|
|
132
|
+
const oldTempFilePath = pageTempFiles.get(pageId);
|
|
133
|
+
if (oldTempFilePath) {
|
|
134
|
+
globalTempFiles.delete(oldTempFilePath);
|
|
135
|
+
try {
|
|
136
|
+
rmSync(oldTempFilePath, {
|
|
137
|
+
force: true
|
|
138
|
+
});
|
|
139
|
+
} catch (error) {}
|
|
140
|
+
}
|
|
141
|
+
const tempFileName = `midscene-dump-${test.testId || uuid()}-${pageId}.json`;
|
|
96
142
|
const tempFilePath = join(tmpdir(), tempFileName);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
type
|
|
103
|
-
description
|
|
104
|
-
|
|
143
|
+
try {
|
|
144
|
+
writeFileSync(tempFilePath, dump, 'utf-8');
|
|
145
|
+
debugPage(`Dump written to temp file: ${tempFilePath}`);
|
|
146
|
+
pageTempFiles.set(pageId, tempFilePath);
|
|
147
|
+
globalTempFiles.add(tempFilePath);
|
|
148
|
+
const currentAnnotation = test.annotations.find((item)=>item.type === midsceneDumpAnnotationId);
|
|
149
|
+
if (currentAnnotation) currentAnnotation.description = tempFilePath;
|
|
150
|
+
else test.annotations.push({
|
|
151
|
+
type: midsceneDumpAnnotationId,
|
|
152
|
+
description: tempFilePath
|
|
153
|
+
});
|
|
154
|
+
} catch (error) {
|
|
155
|
+
debugPage(`Failed to write temp file: ${tempFilePath}. Skipping annotation.`, error);
|
|
156
|
+
}
|
|
105
157
|
};
|
|
106
158
|
return {
|
|
107
159
|
agentForPage: async ({ page }, use, testInfo)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright/ai-fixture.mjs","sources":["webpack://@midscene/web/./src/playwright/ai-fixture.ts"],"sourcesContent":["import { 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\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);\n };\n\n page.on('close', () => {\n debugPage('page closed');\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 = (test: TestInfo, dump: string) => {\n // Write dump to temporary file\n const tempFileName = `midscene-dump-${test.testId || uuid()}-${Date.now()}.json`;\n const tempFilePath = join(tmpdir(), tempFileName);\n\n writeFileSync(tempFilePath, dump, 'utf-8');\n debugPage(`Dump written to temp file: ${tempFilePath}`);\n\n // Store only the file path in annotation\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 };\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","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","tempFileName","Date","tempFilePath","join","tmpdir","writeFileSync","currentAnnotation","item","propsPage","finalCacheConfig","undefined","userCache"],"mappings":"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BACA,cAAc;YAChB;QACF;QACA,WAAW,OACT,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,OAAO,OACL,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,SAAS,OACP,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,oBAAoB,OAClB,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,oBAAoB,OAClB,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,gBAAgB,OACd,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,eAAe,OACb,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,mBAAmB,OACjB,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,qBAAqB,OACnB,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;IACF;AACF"}
|
|
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,4 +1,6 @@
|
|
|
1
|
-
import { readFileSync, rmSync } from "node:fs";
|
|
1
|
+
import { readFileSync, readdirSync, rmSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
2
4
|
import { getReportFileName, printReportMsg } from "@midscene/core/agent";
|
|
3
5
|
import { writeDumpReport } from "@midscene/core/utils";
|
|
4
6
|
import { replaceIllegalPathCharsAndSpace } from "@midscene/shared/utils";
|
|
@@ -55,20 +57,21 @@ class MidsceneReporter {
|
|
|
55
57
|
dumpString = readFileSync(tempFilePath, 'utf-8');
|
|
56
58
|
} catch (error) {
|
|
57
59
|
console.error(`Failed to read Midscene dump file: ${tempFilePath}`, error);
|
|
58
|
-
return;
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
if (dumpString) {
|
|
62
|
+
const retry = result.retry ? `(retry #${result.retry})` : '';
|
|
63
|
+
const testId = `${test.id}${retry}`;
|
|
64
|
+
const testData = {
|
|
65
|
+
dumpString,
|
|
66
|
+
attributes: {
|
|
67
|
+
playwright_test_id: testId,
|
|
68
|
+
playwright_test_title: `${test.title}${retry}`,
|
|
69
|
+
playwright_test_status: result.status,
|
|
70
|
+
playwright_test_duration: result.duration
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
this.updateReport(testData);
|
|
74
|
+
}
|
|
72
75
|
try {
|
|
73
76
|
rmSync(tempFilePath, {
|
|
74
77
|
force: true
|
|
@@ -77,6 +80,30 @@ class MidsceneReporter {
|
|
|
77
80
|
console.warn(`Failed to delete Midscene temp file: ${tempFilePath}`, error);
|
|
78
81
|
}
|
|
79
82
|
}
|
|
83
|
+
onError(error) {
|
|
84
|
+
console.error('Midscene Reporter error occurred:', error);
|
|
85
|
+
}
|
|
86
|
+
onEnd(result) {
|
|
87
|
+
try {
|
|
88
|
+
const tmpDir = tmpdir();
|
|
89
|
+
const files = readdirSync(tmpDir);
|
|
90
|
+
const orphanedFiles = files.filter((f)=>f.startsWith('midscene-dump-'));
|
|
91
|
+
if (orphanedFiles.length > 0) {
|
|
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);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
80
107
|
constructor(options = {}){
|
|
81
108
|
_define_property(this, "mergedFilename", void 0);
|
|
82
109
|
_define_property(this, "testTitleToFilename", new Map());
|
|
@@ -1 +1 @@
|
|
|
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 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;\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 return
|
|
1
|
+
{"version":3,"file":"playwright/reporter/index.mjs","sources":["webpack://@midscene/web/./src/playwright/reporter/index.ts"],"sourcesContent":["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":["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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"}
|
|
@@ -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-20251028065320.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-20251028065320.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-20251028065320.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-20251028065320.0`, 'log');
|
|
88
88
|
}
|
|
89
89
|
async connect() {
|
|
90
90
|
return await this.setupBridgeClient();
|
|
@@ -62,12 +62,48 @@ const groupAndCaseForTest = (testInfo)=>{
|
|
|
62
62
|
};
|
|
63
63
|
const midsceneAgentKeyId = '_midsceneAgentId';
|
|
64
64
|
const midsceneDumpAnnotationId = 'MIDSCENE_DUMP_ANNOTATION';
|
|
65
|
+
const globalTempFiles = new Set();
|
|
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
99
|
const PlaywrightAiFixture = (options)=>{
|
|
66
100
|
const { forceSameTabNavigation = true, waitForNetworkIdleTimeout = constants_namespaceObject.DEFAULT_WAIT_FOR_NETWORK_IDLE_TIMEOUT, waitForNavigationTimeout = constants_namespaceObject.DEFAULT_WAIT_FOR_NAVIGATION_TIMEOUT, cache } = options ?? {};
|
|
67
101
|
const processTestCacheConfig = (testInfo)=>{
|
|
68
102
|
const { id } = groupAndCaseForTest(testInfo);
|
|
69
103
|
return (0, utils_namespaceObject.processCacheConfig)(cache, id);
|
|
70
104
|
};
|
|
105
|
+
const pageTempFiles = new Map();
|
|
106
|
+
registerCleanupHandlers();
|
|
71
107
|
const pageAgentMap = {};
|
|
72
108
|
const createOrReuseAgentForPage = (page, testInfo, opts)=>{
|
|
73
109
|
let idForPage = page[midsceneAgentKeyId];
|
|
@@ -87,10 +123,11 @@ const PlaywrightAiFixture = (options)=>{
|
|
|
87
123
|
...opts
|
|
88
124
|
});
|
|
89
125
|
pageAgentMap[idForPage].onDumpUpdate = (dump)=>{
|
|
90
|
-
updateDumpAnnotation(testInfo, dump);
|
|
126
|
+
updateDumpAnnotation(testInfo, dump, idForPage);
|
|
91
127
|
};
|
|
92
128
|
page.on('close', ()=>{
|
|
93
129
|
debugPage('page closed');
|
|
130
|
+
pageTempFiles.delete(idForPage);
|
|
94
131
|
pageAgentMap[idForPage].destroy();
|
|
95
132
|
delete pageAgentMap[idForPage];
|
|
96
133
|
});
|
|
@@ -120,17 +157,32 @@ const PlaywrightAiFixture = (options)=>{
|
|
|
120
157
|
});
|
|
121
158
|
}));
|
|
122
159
|
}
|
|
123
|
-
const updateDumpAnnotation = (test, dump)=>{
|
|
124
|
-
const
|
|
160
|
+
const updateDumpAnnotation = (test, dump, pageId)=>{
|
|
161
|
+
const oldTempFilePath = pageTempFiles.get(pageId);
|
|
162
|
+
if (oldTempFilePath) {
|
|
163
|
+
globalTempFiles.delete(oldTempFilePath);
|
|
164
|
+
try {
|
|
165
|
+
(0, external_node_fs_namespaceObject.rmSync)(oldTempFilePath, {
|
|
166
|
+
force: true
|
|
167
|
+
});
|
|
168
|
+
} catch (error) {}
|
|
169
|
+
}
|
|
170
|
+
const tempFileName = `midscene-dump-${test.testId || (0, shared_utils_namespaceObject.uuid)()}-${pageId}.json`;
|
|
125
171
|
const tempFilePath = (0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), tempFileName);
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
type
|
|
132
|
-
description
|
|
133
|
-
|
|
172
|
+
try {
|
|
173
|
+
(0, external_node_fs_namespaceObject.writeFileSync)(tempFilePath, dump, 'utf-8');
|
|
174
|
+
debugPage(`Dump written to temp file: ${tempFilePath}`);
|
|
175
|
+
pageTempFiles.set(pageId, tempFilePath);
|
|
176
|
+
globalTempFiles.add(tempFilePath);
|
|
177
|
+
const currentAnnotation = test.annotations.find((item)=>item.type === midsceneDumpAnnotationId);
|
|
178
|
+
if (currentAnnotation) currentAnnotation.description = tempFilePath;
|
|
179
|
+
else test.annotations.push({
|
|
180
|
+
type: midsceneDumpAnnotationId,
|
|
181
|
+
description: tempFilePath
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
debugPage(`Failed to write temp file: ${tempFilePath}. Skipping annotation.`, error);
|
|
185
|
+
}
|
|
134
186
|
};
|
|
135
187
|
return {
|
|
136
188
|
agentForPage: async ({ page }, use, testInfo)=>{
|
|
@@ -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 { 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\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);\n };\n\n page.on('close', () => {\n debugPage('page closed');\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 = (test: TestInfo, dump: string) => {\n // Write dump to temporary file\n const tempFileName = `midscene-dump-${test.testId || uuid()}-${Date.now()}.json`;\n const tempFilePath = join(tmpdir(), tempFileName);\n\n writeFileSync(tempFilePath, dump, 'utf-8');\n debugPage(`Dump written to temp file: ${tempFilePath}`);\n\n // Store only the file path in annotation\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 };\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","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","tempFileName","Date","tempFilePath","join","tmpdir","writeFileSync","currentAnnotation","item","propsPage","finalCacheConfig","undefined","userCache"],"mappings":"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jC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,oBAAoB,OAClB,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,gBAAgB,OACd,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,eAAe,OACb,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,mBAAmB,OACjB,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;gBACA,cAAc;YAChB;QACF;QACA,qBAAqB,OACnB,EAAEZ,IAAI,EAAkC,EACxCY,KACAjC;YAEA,MAAMgC,mBAAmB;gBACvBX;gBACArB;gBACAiC;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// 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"}
|
|
@@ -27,6 +27,8 @@ __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");
|
|
30
32
|
const agent_namespaceObject = require("@midscene/core/agent");
|
|
31
33
|
const utils_namespaceObject = require("@midscene/core/utils");
|
|
32
34
|
const shared_utils_namespaceObject = require("@midscene/shared/utils");
|
|
@@ -83,20 +85,21 @@ class MidsceneReporter {
|
|
|
83
85
|
dumpString = (0, external_node_fs_namespaceObject.readFileSync)(tempFilePath, 'utf-8');
|
|
84
86
|
} catch (error) {
|
|
85
87
|
console.error(`Failed to read Midscene dump file: ${tempFilePath}`, error);
|
|
86
|
-
return;
|
|
87
88
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
89
|
+
if (dumpString) {
|
|
90
|
+
const retry = result.retry ? `(retry #${result.retry})` : '';
|
|
91
|
+
const testId = `${test.id}${retry}`;
|
|
92
|
+
const testData = {
|
|
93
|
+
dumpString,
|
|
94
|
+
attributes: {
|
|
95
|
+
playwright_test_id: testId,
|
|
96
|
+
playwright_test_title: `${test.title}${retry}`,
|
|
97
|
+
playwright_test_status: result.status,
|
|
98
|
+
playwright_test_duration: result.duration
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
this.updateReport(testData);
|
|
102
|
+
}
|
|
100
103
|
try {
|
|
101
104
|
(0, external_node_fs_namespaceObject.rmSync)(tempFilePath, {
|
|
102
105
|
force: true
|
|
@@ -105,6 +108,30 @@ class MidsceneReporter {
|
|
|
105
108
|
console.warn(`Failed to delete Midscene temp file: ${tempFilePath}`, error);
|
|
106
109
|
}
|
|
107
110
|
}
|
|
111
|
+
onError(error) {
|
|
112
|
+
console.error('Midscene Reporter error occurred:', error);
|
|
113
|
+
}
|
|
114
|
+
onEnd(result) {
|
|
115
|
+
try {
|
|
116
|
+
const tmpDir = (0, external_node_os_namespaceObject.tmpdir)();
|
|
117
|
+
const files = (0, external_node_fs_namespaceObject.readdirSync)(tmpDir);
|
|
118
|
+
const orphanedFiles = files.filter((f)=>f.startsWith('midscene-dump-'));
|
|
119
|
+
if (orphanedFiles.length > 0) {
|
|
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);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
108
135
|
constructor(options = {}){
|
|
109
136
|
_define_property(this, "mergedFilename", void 0);
|
|
110
137
|
_define_property(this, "testTitleToFilename", new Map());
|
|
@@ -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, 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 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;\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 return;\n }\n\n const retry = result.retry ? `(retry #${result.retry})` : '';\n const testId = `${test.id}${retry}`;\n const testData: ReportDumpWithAttributes = {\n dumpString,\n attributes: {\n playwright_test_id: testId,\n playwright_test_title: `${test.title}${retry}`,\n playwright_test_status: result.status,\n playwright_test_duration: result.duration,\n },\n };\n\n this.updateReport(testData);\n\n // Clean up: delete temp file\n try {\n rmSync(tempFilePath, { force: true });\n } catch (error) {\n console.warn(\n `Failed to delete Midscene temp file: ${tempFilePath}`,\n error,\n );\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","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, 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,4 +1,4 @@
|
|
|
1
|
-
import type { FullConfig, Reporter, Suite, TestCase, TestResult } from '@playwright/test/reporter';
|
|
1
|
+
import type { FullConfig, FullResult, Reporter, Suite, TestCase, TestError, TestResult } from '@playwright/test/reporter';
|
|
2
2
|
interface MidsceneReporterOptions {
|
|
3
3
|
type?: 'merged' | 'separate';
|
|
4
4
|
}
|
|
@@ -14,5 +14,7 @@ declare class MidsceneReporter implements Reporter {
|
|
|
14
14
|
onBegin(config: FullConfig, suite: Suite): Promise<void>;
|
|
15
15
|
onTestBegin(_test: TestCase, _result: TestResult): void;
|
|
16
16
|
onTestEnd(test: TestCase, result: TestResult): void;
|
|
17
|
+
onError(error: TestError): void;
|
|
18
|
+
onEnd(result: FullResult): void;
|
|
17
19
|
}
|
|
18
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-20251028065320.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/
|
|
108
|
-
"@midscene/
|
|
106
|
+
"@midscene/core": "1.0.1-beta-20251028065320.0",
|
|
107
|
+
"@midscene/shared": "1.0.1-beta-20251028065320.0",
|
|
108
|
+
"@midscene/playground": "1.0.1-beta-20251028065320.0"
|
|
109
109
|
},
|
|
110
110
|
"devDependencies": {
|
|
111
111
|
"@playwright/test": "^1.44.1",
|