@midscene/test 1.17.0 → 1.17.1
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/midscene/index.mjs +9 -2
- package/dist/es/midscene/index.mjs.map +1 -1
- package/dist/es/test-cli.mjs +1 -1
- package/dist/lib/midscene/index.js +9 -2
- package/dist/lib/midscene/index.js.map +1 -1
- package/dist/lib/test-cli.js +1 -1
- package/dist/types/midscene/index.d.ts +5 -0
- package/package.json +13 -13
|
@@ -142,6 +142,8 @@ const warnReportTrace = getDebug('test-runner:report-trace', {
|
|
|
142
142
|
});
|
|
143
143
|
function createMidsceneNodes(options) {
|
|
144
144
|
if (!options || 'object' != typeof options) throw new NodeDefinitionError('createMidsceneNodes() options must be an object.');
|
|
145
|
+
const { namespace } = options;
|
|
146
|
+
if (void 0 !== namespace && ('string' != typeof namespace || namespace.split('.').some((part)=>!part || /\s/.test(part)))) throw new NodeDefinitionError('createMidsceneNodes() namespace must be a non-empty dot-separated string without whitespace.');
|
|
145
147
|
if ('function' != typeof options.agentProvider?.getAgent && 'function' != typeof options.getAgent) throw new NodeDefinitionError('createMidsceneNodes() requires getAgent or agentProvider.getAgent.');
|
|
146
148
|
if (!options.agentClass || 'function' != typeof options.agentClass.getTestRunnerNodeDefinitions) throw new NodeDefinitionError('createMidsceneNodes() requires agentClass.getTestRunnerNodeDefinitions().');
|
|
147
149
|
const agentsByScope = new Map();
|
|
@@ -173,11 +175,16 @@ function createMidsceneNodes(options) {
|
|
|
173
175
|
return agent;
|
|
174
176
|
}), ctx.signal, 'acquisition');
|
|
175
177
|
};
|
|
176
|
-
const
|
|
178
|
+
const nodeName = (name)=>void 0 === namespace ? name : `${namespace}.${name}`;
|
|
179
|
+
const definitions = options.agentClass.getTestRunnerNodeDefinitions();
|
|
180
|
+
const agentDefinitions = void 0 === namespace ? definitions : definitions.map((definition)=>({
|
|
181
|
+
...definition,
|
|
182
|
+
name: nodeName(definition.name)
|
|
183
|
+
}));
|
|
177
184
|
return [
|
|
178
185
|
...createAgentTestRunnerNodes(agentDefinitions, getAgent),
|
|
179
186
|
define_node_defineNode({
|
|
180
|
-
name: 'wait',
|
|
187
|
+
name: nodeName('wait'),
|
|
181
188
|
description: 'Wait for a fixed duration while honoring cancellation.',
|
|
182
189
|
stringInputKey: false,
|
|
183
190
|
inputSchema: waitInputSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midscene/index.mjs","sources":["../../../src/node/define-node.ts","../../../src/midscene/index.ts"],"sourcesContent":["import { defineNode as coreDefineNode } from '@midscene/core/internal/test-runner';\nimport type {\n DefineNodeOptions,\n DefineNodeWithSchemaOptions,\n NodeDefinition,\n NodeDefinitionWithSchema,\n NodeInputSchema,\n} from './types';\n\n// A type-only facade: validation and execution still belong to the shared kernel.\ninterface DefineNativeNode {\n <TSchema extends NodeInputSchema, TData = unknown, TContext = unknown>(\n options: DefineNodeWithSchemaOptions<TSchema, TData, TContext>,\n ): NodeDefinitionWithSchema<TSchema, TData, TContext>;\n <TInput = unknown, TData = unknown, TContext = unknown>(\n options: DefineNodeOptions<TInput, TData, TContext>,\n ): NodeDefinition<TInput, TData, TContext>;\n}\nexport const defineNode: DefineNativeNode = coreDefineNode;\n","import type { IExecutionDump } from '@midscene/core';\nimport {\n type AgentTestRunnerNodeDefinition,\n type AgentTestRunnerNodeProvider,\n type CommonAgentTestRunnerApi,\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiTapInputSchema,\n aiWaitForInputSchema,\n aiWaitForOptionsInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n} from '@midscene/core/agent/test';\nimport {\n type WorkflowReportSource,\n admitResourceOperation,\n collectTestRunReportMetrics,\n hasPendingResourceOperation,\n retainResourceForScope,\n trackResourceOperation,\n} from '@midscene/core/internal/test-runner';\nimport {\n enterYamlAction,\n enterYamlExecution,\n runInYamlExecutionContext,\n} from '@midscene/core/internal/yaml-runtime';\nimport { getDebug } from '@midscene/shared/logger';\nimport { z } from 'zod/v4';\nimport type { Awaitable } from '../engine/types';\nimport { NodeDefinitionError } from '../errors';\nimport { defineNode } from '../node/define-node';\nimport type { NodeDefinition, NodeExecutionContext } from '../node/types';\n\nexport type MidsceneUIAgent = CommonAgentTestRunnerApi & {\n _prepareForTestRunner?(): void;\n _createReportSource?(\n scopeId: string,\n executionIds?: readonly string[],\n ): Promise<WorkflowReportSource | undefined>;\n flushReport?(): Promise<string | undefined>;\n addDumpUpdateListener?(\n listener: (dump: string, execution?: MidsceneExecutionRef) => void,\n ): () => void;\n};\n\nexport {\n actionInputSchema,\n aiDragAndDropInputSchema,\n aiInputInputSchema,\n aiKeyboardPressInputSchema,\n aiLongPressInputSchema,\n aiPinchInputSchema,\n aiQueryInputSchema,\n aiScrollInputSchema,\n javascriptInputSchema,\n runGherkinScenarioInputSchema,\n setAIContextInputSchema,\n} from '@midscene/core/agent/test';\nexport type {\n ActionNodeInput,\n AiDragAndDropNodeInput,\n AiInputNodeInput,\n AiKeyboardPressNodeInput,\n AiLongPressNodeInput,\n AiPinchNodeInput,\n AiQueryNodeInput,\n AiScrollNodeInput,\n JavascriptNodeInput,\n RunGherkinScenarioNodeInput,\n SetAIContextNodeInput,\n} from '@midscene/core/agent/test';\n\nexport {\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiWaitForInputSchema,\n aiWaitForOptionsInputSchema,\n aiTapInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n};\n\nexport interface MidsceneExecutionRef {\n id?: string;\n tasks?: IExecutionDump['tasks'];\n}\n\nexport interface AgentProvider<TContext> {\n getAgent(\n runId: string,\n execution: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n // biome-ignore lint/suspicious/noConfusingVoidType: providers without a report intentionally return void.\n releaseAgent?(runId: string): Awaitable<AgentReleaseResult | void>;\n dispose?(): Awaitable<void>;\n}\n\nexport interface AgentReleaseResult {\n /** Absolute path to the finalized report for this Agent scope. */\n reportPath?: string;\n}\n\nexport const waitInputSchema = z.strictObject({\n duration: z.number().positive().describe('How long to wait.'),\n unit: z\n .enum(['ms', 's', 'min'])\n .default('ms')\n .describe('Duration unit: milliseconds, seconds, or minutes.'),\n});\n\nexport type AiActNodeInput = z.infer<typeof aiActInputSchema>;\nexport type AiAssertNodeInput = z.infer<typeof aiAssertInputSchema>;\nexport type AiWaitForNodeInput = z.infer<typeof aiWaitForInputSchema>;\nexport type AiWaitForNodeOptions = z.infer<typeof aiWaitForOptionsInputSchema>;\nexport type AiTapNodeInput = z.infer<typeof aiTapInputSchema>;\nexport type InsightNodeInput = z.infer<typeof insightInputSchema>;\nexport type RecordToReportNodeInput = z.infer<typeof recordToReportInputSchema>;\nexport type WaitNodeInput = z.infer<typeof waitInputSchema>;\n\nexport interface CreateMidsceneNodesOptions<TContext> {\n getAgent?(\n execution: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n agentProvider?: AgentProvider<TContext>;\n /** Agent class that declares the Agent-backed Nodes to register. */\n agentClass: AgentTestRunnerNodeProvider;\n}\n\nconst waitFor = async (durationMs: number, signal: AbortSignal) => {\n if (signal.aborted) {\n throw signal.reason ?? new Error('Wait aborted.');\n }\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n signal.removeEventListener('abort', abort);\n resolve();\n }, durationMs);\n const abort = () => {\n clearTimeout(timeout);\n reject(signal.reason ?? new Error('Wait aborted.'));\n };\n signal.addEventListener('abort', abort, { once: true });\n });\n};\n\n/** Adapt Agent-owned descriptions to executable Midscene Test Nodes. */\nexport const createAgentTestRunnerNodes = <TContext>(\n definitions: readonly AgentTestRunnerNodeDefinition[],\n getAgent: (\n ctx: NodeExecutionContext<unknown, TContext>,\n ) => Awaitable<unknown>,\n): readonly NodeDefinition<any, any, TContext>[] => {\n const runAgentCall = async <T>(\n node: string,\n ctx: NodeExecutionContext<unknown, TContext>,\n agent: MidsceneUIAgent,\n call: () => Promise<T>,\n captureExecutions = true,\n ): Promise<T> =>\n runInYamlExecutionContext(async () => {\n const scopeId =\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n const isCleanupOwner = admitResourceOperation(agent, ctx.signal);\n if (captureExecutions) {\n ctx.report.onSnapshot(async (signal, traces) => {\n signal.throwIfAborted();\n const partial = hasPendingResourceOperation(agent);\n const capturedAt = new Date().toISOString();\n let source = await agent._createReportSource?.(\n scopeId,\n traces.map((trace) => trace.executionId),\n );\n // External adapters may only expose HTML. Capture it independently\n // of cleanup, while marking its mutable source as partial.\n if (!source) {\n const path = await agent.flushReport?.();\n if (path)\n source = {\n sourcePath: path,\n collection: {\n status: 'partial',\n capturedAt,\n reason:\n 'This provider exposes live HTML without immutable snapshot support.',\n },\n };\n }\n signal.throwIfAborted();\n if (!source)\n throw new Error('Agent did not return a report snapshot.');\n return {\n ...source,\n collection: {\n status:\n partial || source.collection?.status === 'partial'\n ? ('partial' as const)\n : ('complete' as const),\n capturedAt: source.collection?.capturedAt ?? capturedAt,\n ...(source.collection?.reason\n ? { reason: source.collection.reason }\n : partial\n ? {\n reason:\n 'An Agent operation was still running at the snapshot cutoff.',\n }\n : {}),\n },\n };\n });\n }\n // Native Steps own the root report. Any Agent.runYaml invoked underneath\n // contributes to that Step instead of publishing another root snapshot.\n const yamlBoundary = enterYamlExecution(agent, {\n allowIndependentOverlap: isCleanupOwner,\n });\n const leaveYamlAction = enterYamlAction(agent, ctx.signal);\n let removeListener: (() => void) | undefined;\n let sawExecutionWithoutId = false;\n const stopListening = () => {\n const remove = removeListener;\n removeListener = undefined;\n remove?.();\n };\n const stopListeningOnAbort = () => stopListening();\n try {\n if (captureExecutions && agent.addDumpUpdateListener) {\n removeListener = agent.addDumpUpdateListener((_dump, execution) => {\n if (!execution) return;\n if (!execution.id) {\n sawExecutionWithoutId = true;\n return;\n }\n ctx.report.addTrace({\n type: 'midscene-execution',\n executionId: execution.id,\n ...(execution.tasks\n ? {\n metrics: collectTestRunReportMetrics({\n executions: [\n {\n id: execution.id,\n name: node,\n logTime: 0,\n tasks: execution.tasks,\n },\n ],\n }),\n }\n : {}),\n });\n });\n if (ctx.signal.aborted) stopListening();\n else {\n ctx.signal.addEventListener('abort', stopListeningOnAbort, {\n once: true,\n });\n }\n }\n return await trackResourceOperation(\n agent,\n Promise.resolve().then(call),\n ctx.signal,\n );\n } finally {\n leaveYamlAction();\n yamlBoundary.finish();\n ctx.signal.removeEventListener('abort', stopListeningOnAbort);\n stopListening();\n if (sawExecutionWithoutId) {\n warnReportTrace(\n `Skipped a report trace without a stable execution id for ${node} in scope ${scopeId}.`,\n );\n }\n }\n });\n\n return definitions.map((definition) =>\n defineNode({\n name: definition.name,\n ...(definition.title === undefined ? {} : { title: definition.title }),\n ...(definition.description === undefined\n ? {}\n : { description: definition.description }),\n ...(definition.stringInputKey === undefined\n ? {}\n : { stringInputKey: definition.stringInputKey }),\n inputSchema: definition.inputSchema,\n async execute(ctx) {\n const agent = await getAgent(ctx);\n ctx.signal.throwIfAborted();\n const call = async () =>\n definition.execute(agent, ctx.input, { signal: ctx.signal });\n if (typeof agent !== 'object' || agent === null) return call();\n return runAgentCall(\n definition.name,\n ctx,\n agent as MidsceneUIAgent,\n call,\n );\n },\n }),\n );\n};\n\nconst warnReportTrace = getDebug('test-runner:report-trace', {\n console: true,\n});\n\nexport function createMidsceneNodes<TContext>(\n options: CreateMidsceneNodesOptions<TContext>,\n): readonly NodeDefinition<any, any, TContext>[] {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() options must be an object.',\n );\n }\n if (\n typeof options.agentProvider?.getAgent !== 'function' &&\n typeof options.getAgent !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires getAgent or agentProvider.getAgent.',\n );\n }\n if (\n !options.agentClass ||\n typeof options.agentClass.getTestRunnerNodeDefinitions !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires agentClass.getTestRunnerNodeDefinitions().',\n );\n }\n\n const agentsByScope = new Map<string, Set<MidsceneUIAgent>>();\n const getAgent = async (\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Promise<MidsceneUIAgent> => {\n const runId =\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n let agents = agentsByScope.get(runId);\n if (!agents) {\n agents = new Set();\n agentsByScope.set(runId, agents);\n ctx.onTeardown(async () => {\n try {\n const released = await options.agentProvider?.releaseAgent?.(runId);\n return released?.reportPath\n ? { reportPaths: [released.reportPath] }\n : undefined;\n } finally {\n agentsByScope.delete(runId);\n }\n });\n }\n const scopeAgents = agents;\n // Acquisition can outlive cancellation. Teardown waits for actual settlement.\n return trackResourceOperation(\n {},\n Promise.resolve().then(async () => {\n const agent = options.agentProvider\n ? await options.agentProvider.getAgent(runId, ctx)\n : await options.getAgent!(ctx);\n retainResourceForScope(agent, ctx.signal);\n agent._prepareForTestRunner?.();\n scopeAgents.add(agent);\n return agent;\n }),\n ctx.signal,\n 'acquisition',\n );\n };\n\n const agentDefinitions = options.agentClass.getTestRunnerNodeDefinitions();\n\n return [\n ...createAgentTestRunnerNodes(agentDefinitions, getAgent),\n defineNode<typeof waitInputSchema, unknown, TContext>({\n name: 'wait',\n description: 'Wait for a fixed duration while honoring cancellation.',\n stringInputKey: false,\n inputSchema: waitInputSchema,\n async execute(execution) {\n const multiplier =\n execution.input.unit === 'min'\n ? 60_000\n : execution.input.unit === 's'\n ? 1_000\n : 1;\n const durationMs = execution.input.duration * multiplier;\n await waitFor(durationMs, execution.signal);\n return { summary: `Waited ${durationMs}ms` };\n },\n }),\n ];\n}\n"],"names":["defineNode","coreDefineNode","waitInputSchema","z","waitFor","durationMs","signal","Error","Promise","resolve","reject","timeout","setTimeout","abort","clearTimeout","createAgentTestRunnerNodes","definitions","getAgent","runAgentCall","node","ctx","agent","call","captureExecutions","runInYamlExecutionContext","scopeId","isCleanupOwner","admitResourceOperation","traces","partial","hasPendingResourceOperation","capturedAt","Date","source","trace","path","yamlBoundary","enterYamlExecution","leaveYamlAction","enterYamlAction","removeListener","sawExecutionWithoutId","stopListening","remove","undefined","stopListeningOnAbort","_dump","execution","collectTestRunReportMetrics","trackResourceOperation","warnReportTrace","definition","getDebug","createMidsceneNodes","options","NodeDefinitionError","agentsByScope","Map","runId","agents","Set","released","scopeAgents","retainResourceForScope","agentDefinitions","multiplier"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"midscene/index.mjs","sources":["../../../src/node/define-node.ts","../../../src/midscene/index.ts"],"sourcesContent":["import { defineNode as coreDefineNode } from '@midscene/core/internal/test-runner';\nimport type {\n DefineNodeOptions,\n DefineNodeWithSchemaOptions,\n NodeDefinition,\n NodeDefinitionWithSchema,\n NodeInputSchema,\n} from './types';\n\n// A type-only facade: validation and execution still belong to the shared kernel.\ninterface DefineNativeNode {\n <TSchema extends NodeInputSchema, TData = unknown, TContext = unknown>(\n options: DefineNodeWithSchemaOptions<TSchema, TData, TContext>,\n ): NodeDefinitionWithSchema<TSchema, TData, TContext>;\n <TInput = unknown, TData = unknown, TContext = unknown>(\n options: DefineNodeOptions<TInput, TData, TContext>,\n ): NodeDefinition<TInput, TData, TContext>;\n}\nexport const defineNode: DefineNativeNode = coreDefineNode;\n","import type { IExecutionDump } from '@midscene/core';\nimport {\n type AgentTestRunnerNodeDefinition,\n type AgentTestRunnerNodeProvider,\n type CommonAgentTestRunnerApi,\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiTapInputSchema,\n aiWaitForInputSchema,\n aiWaitForOptionsInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n} from '@midscene/core/agent/test';\nimport {\n type WorkflowReportSource,\n admitResourceOperation,\n collectTestRunReportMetrics,\n hasPendingResourceOperation,\n retainResourceForScope,\n trackResourceOperation,\n} from '@midscene/core/internal/test-runner';\nimport {\n enterYamlAction,\n enterYamlExecution,\n runInYamlExecutionContext,\n} from '@midscene/core/internal/yaml-runtime';\nimport { getDebug } from '@midscene/shared/logger';\nimport { z } from 'zod/v4';\nimport type { Awaitable } from '../engine/types';\nimport { NodeDefinitionError } from '../errors';\nimport { defineNode } from '../node/define-node';\nimport type { NodeDefinition, NodeExecutionContext } from '../node/types';\n\nexport type MidsceneUIAgent = CommonAgentTestRunnerApi & {\n _prepareForTestRunner?(): void;\n _createReportSource?(\n scopeId: string,\n executionIds?: readonly string[],\n ): Promise<WorkflowReportSource | undefined>;\n flushReport?(): Promise<string | undefined>;\n addDumpUpdateListener?(\n listener: (dump: string, execution?: MidsceneExecutionRef) => void,\n ): () => void;\n};\n\nexport {\n actionInputSchema,\n aiDragAndDropInputSchema,\n aiInputInputSchema,\n aiKeyboardPressInputSchema,\n aiLongPressInputSchema,\n aiPinchInputSchema,\n aiQueryInputSchema,\n aiScrollInputSchema,\n javascriptInputSchema,\n runGherkinScenarioInputSchema,\n setAIContextInputSchema,\n} from '@midscene/core/agent/test';\nexport type {\n ActionNodeInput,\n AiDragAndDropNodeInput,\n AiInputNodeInput,\n AiKeyboardPressNodeInput,\n AiLongPressNodeInput,\n AiPinchNodeInput,\n AiQueryNodeInput,\n AiScrollNodeInput,\n JavascriptNodeInput,\n RunGherkinScenarioNodeInput,\n SetAIContextNodeInput,\n} from '@midscene/core/agent/test';\n\nexport {\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiWaitForInputSchema,\n aiWaitForOptionsInputSchema,\n aiTapInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n};\n\nexport interface MidsceneExecutionRef {\n id?: string;\n tasks?: IExecutionDump['tasks'];\n}\n\nexport interface AgentProvider<TContext> {\n getAgent(\n runId: string,\n execution: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n // biome-ignore lint/suspicious/noConfusingVoidType: providers without a report intentionally return void.\n releaseAgent?(runId: string): Awaitable<AgentReleaseResult | void>;\n dispose?(): Awaitable<void>;\n}\n\nexport interface AgentReleaseResult {\n /** Absolute path to the finalized report for this Agent scope. */\n reportPath?: string;\n}\n\nexport const waitInputSchema = z.strictObject({\n duration: z.number().positive().describe('How long to wait.'),\n unit: z\n .enum(['ms', 's', 'min'])\n .default('ms')\n .describe('Duration unit: milliseconds, seconds, or minutes.'),\n});\n\nexport type AiActNodeInput = z.infer<typeof aiActInputSchema>;\nexport type AiAssertNodeInput = z.infer<typeof aiAssertInputSchema>;\nexport type AiWaitForNodeInput = z.infer<typeof aiWaitForInputSchema>;\nexport type AiWaitForNodeOptions = z.infer<typeof aiWaitForOptionsInputSchema>;\nexport type AiTapNodeInput = z.infer<typeof aiTapInputSchema>;\nexport type InsightNodeInput = z.infer<typeof insightInputSchema>;\nexport type RecordToReportNodeInput = z.infer<typeof recordToReportInputSchema>;\nexport type WaitNodeInput = z.infer<typeof waitInputSchema>;\n\nexport interface CreateMidsceneNodesOptions<TContext> {\n /**\n * Prefix every returned Node name, including wait, with `${namespace}.`.\n * Use non-empty dot-separated segments without whitespace.\n */\n namespace?: string;\n getAgent?(\n execution: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n agentProvider?: AgentProvider<TContext>;\n /** Agent class that declares the Agent-backed Nodes to register. */\n agentClass: AgentTestRunnerNodeProvider;\n}\n\nconst waitFor = async (durationMs: number, signal: AbortSignal) => {\n if (signal.aborted) {\n throw signal.reason ?? new Error('Wait aborted.');\n }\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n signal.removeEventListener('abort', abort);\n resolve();\n }, durationMs);\n const abort = () => {\n clearTimeout(timeout);\n reject(signal.reason ?? new Error('Wait aborted.'));\n };\n signal.addEventListener('abort', abort, { once: true });\n });\n};\n\n/** Adapt Agent-owned descriptions to executable Midscene Test Nodes. */\nexport const createAgentTestRunnerNodes = <TContext>(\n definitions: readonly AgentTestRunnerNodeDefinition[],\n getAgent: (\n ctx: NodeExecutionContext<unknown, TContext>,\n ) => Awaitable<unknown>,\n): readonly NodeDefinition<any, any, TContext>[] => {\n const runAgentCall = async <T>(\n node: string,\n ctx: NodeExecutionContext<unknown, TContext>,\n agent: MidsceneUIAgent,\n call: () => Promise<T>,\n captureExecutions = true,\n ): Promise<T> =>\n runInYamlExecutionContext(async () => {\n const scopeId =\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n const isCleanupOwner = admitResourceOperation(agent, ctx.signal);\n if (captureExecutions) {\n ctx.report.onSnapshot(async (signal, traces) => {\n signal.throwIfAborted();\n const partial = hasPendingResourceOperation(agent);\n const capturedAt = new Date().toISOString();\n let source = await agent._createReportSource?.(\n scopeId,\n traces.map((trace) => trace.executionId),\n );\n // External adapters may only expose HTML. Capture it independently\n // of cleanup, while marking its mutable source as partial.\n if (!source) {\n const path = await agent.flushReport?.();\n if (path)\n source = {\n sourcePath: path,\n collection: {\n status: 'partial',\n capturedAt,\n reason:\n 'This provider exposes live HTML without immutable snapshot support.',\n },\n };\n }\n signal.throwIfAborted();\n if (!source)\n throw new Error('Agent did not return a report snapshot.');\n return {\n ...source,\n collection: {\n status:\n partial || source.collection?.status === 'partial'\n ? ('partial' as const)\n : ('complete' as const),\n capturedAt: source.collection?.capturedAt ?? capturedAt,\n ...(source.collection?.reason\n ? { reason: source.collection.reason }\n : partial\n ? {\n reason:\n 'An Agent operation was still running at the snapshot cutoff.',\n }\n : {}),\n },\n };\n });\n }\n // Native Steps own the root report. Any Agent.runYaml invoked underneath\n // contributes to that Step instead of publishing another root snapshot.\n const yamlBoundary = enterYamlExecution(agent, {\n allowIndependentOverlap: isCleanupOwner,\n });\n const leaveYamlAction = enterYamlAction(agent, ctx.signal);\n let removeListener: (() => void) | undefined;\n let sawExecutionWithoutId = false;\n const stopListening = () => {\n const remove = removeListener;\n removeListener = undefined;\n remove?.();\n };\n const stopListeningOnAbort = () => stopListening();\n try {\n if (captureExecutions && agent.addDumpUpdateListener) {\n removeListener = agent.addDumpUpdateListener((_dump, execution) => {\n if (!execution) return;\n if (!execution.id) {\n sawExecutionWithoutId = true;\n return;\n }\n ctx.report.addTrace({\n type: 'midscene-execution',\n executionId: execution.id,\n ...(execution.tasks\n ? {\n metrics: collectTestRunReportMetrics({\n executions: [\n {\n id: execution.id,\n name: node,\n logTime: 0,\n tasks: execution.tasks,\n },\n ],\n }),\n }\n : {}),\n });\n });\n if (ctx.signal.aborted) stopListening();\n else {\n ctx.signal.addEventListener('abort', stopListeningOnAbort, {\n once: true,\n });\n }\n }\n return await trackResourceOperation(\n agent,\n Promise.resolve().then(call),\n ctx.signal,\n );\n } finally {\n leaveYamlAction();\n yamlBoundary.finish();\n ctx.signal.removeEventListener('abort', stopListeningOnAbort);\n stopListening();\n if (sawExecutionWithoutId) {\n warnReportTrace(\n `Skipped a report trace without a stable execution id for ${node} in scope ${scopeId}.`,\n );\n }\n }\n });\n\n return definitions.map((definition) =>\n defineNode({\n name: definition.name,\n ...(definition.title === undefined ? {} : { title: definition.title }),\n ...(definition.description === undefined\n ? {}\n : { description: definition.description }),\n ...(definition.stringInputKey === undefined\n ? {}\n : { stringInputKey: definition.stringInputKey }),\n inputSchema: definition.inputSchema,\n async execute(ctx) {\n const agent = await getAgent(ctx);\n ctx.signal.throwIfAborted();\n const call = async () =>\n definition.execute(agent, ctx.input, { signal: ctx.signal });\n if (typeof agent !== 'object' || agent === null) return call();\n return runAgentCall(\n definition.name,\n ctx,\n agent as MidsceneUIAgent,\n call,\n );\n },\n }),\n );\n};\n\nconst warnReportTrace = getDebug('test-runner:report-trace', {\n console: true,\n});\n\nexport function createMidsceneNodes<TContext>(\n options: CreateMidsceneNodesOptions<TContext>,\n): readonly NodeDefinition<any, any, TContext>[] {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() options must be an object.',\n );\n }\n const { namespace } = options;\n if (\n namespace !== undefined &&\n (typeof namespace !== 'string' ||\n namespace.split('.').some((part) => !part || /\\s/.test(part)))\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() namespace must be a non-empty dot-separated string without whitespace.',\n );\n }\n if (\n typeof options.agentProvider?.getAgent !== 'function' &&\n typeof options.getAgent !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires getAgent or agentProvider.getAgent.',\n );\n }\n if (\n !options.agentClass ||\n typeof options.agentClass.getTestRunnerNodeDefinitions !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires agentClass.getTestRunnerNodeDefinitions().',\n );\n }\n\n const agentsByScope = new Map<string, Set<MidsceneUIAgent>>();\n const getAgent = async (\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Promise<MidsceneUIAgent> => {\n const runId =\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n let agents = agentsByScope.get(runId);\n if (!agents) {\n agents = new Set();\n agentsByScope.set(runId, agents);\n ctx.onTeardown(async () => {\n try {\n const released = await options.agentProvider?.releaseAgent?.(runId);\n return released?.reportPath\n ? { reportPaths: [released.reportPath] }\n : undefined;\n } finally {\n agentsByScope.delete(runId);\n }\n });\n }\n const scopeAgents = agents;\n // Acquisition can outlive cancellation. Teardown waits for actual settlement.\n return trackResourceOperation(\n {},\n Promise.resolve().then(async () => {\n const agent = options.agentProvider\n ? await options.agentProvider.getAgent(runId, ctx)\n : await options.getAgent!(ctx);\n retainResourceForScope(agent, ctx.signal);\n agent._prepareForTestRunner?.();\n scopeAgents.add(agent);\n return agent;\n }),\n ctx.signal,\n 'acquisition',\n );\n };\n\n const nodeName = (name: string) =>\n namespace === undefined ? name : `${namespace}.${name}`;\n const definitions = options.agentClass.getTestRunnerNodeDefinitions();\n const agentDefinitions =\n namespace === undefined\n ? definitions\n : definitions.map((definition) => ({\n ...definition,\n name: nodeName(definition.name),\n }));\n\n return [\n ...createAgentTestRunnerNodes(agentDefinitions, getAgent),\n defineNode<typeof waitInputSchema, unknown, TContext>({\n name: nodeName('wait'),\n description: 'Wait for a fixed duration while honoring cancellation.',\n stringInputKey: false,\n inputSchema: waitInputSchema,\n async execute(execution) {\n const multiplier =\n execution.input.unit === 'min'\n ? 60_000\n : execution.input.unit === 's'\n ? 1_000\n : 1;\n const durationMs = execution.input.duration * multiplier;\n await waitFor(durationMs, execution.signal);\n return { summary: `Waited ${durationMs}ms` };\n },\n }),\n ];\n}\n"],"names":["defineNode","coreDefineNode","waitInputSchema","z","waitFor","durationMs","signal","Error","Promise","resolve","reject","timeout","setTimeout","abort","clearTimeout","createAgentTestRunnerNodes","definitions","getAgent","runAgentCall","node","ctx","agent","call","captureExecutions","runInYamlExecutionContext","scopeId","isCleanupOwner","admitResourceOperation","traces","partial","hasPendingResourceOperation","capturedAt","Date","source","trace","path","yamlBoundary","enterYamlExecution","leaveYamlAction","enterYamlAction","removeListener","sawExecutionWithoutId","stopListening","remove","undefined","stopListeningOnAbort","_dump","execution","collectTestRunReportMetrics","trackResourceOperation","warnReportTrace","definition","getDebug","createMidsceneNodes","options","NodeDefinitionError","namespace","part","agentsByScope","Map","runId","agents","Set","released","scopeAgents","retainResourceForScope","nodeName","name","agentDefinitions","multiplier"],"mappings":"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"}
|
package/dist/es/test-cli.mjs
CHANGED
|
@@ -199,6 +199,8 @@ const warnReportTrace = (0, logger_namespaceObject.getDebug)('test-runner:report
|
|
|
199
199
|
});
|
|
200
200
|
function createMidsceneNodes(options) {
|
|
201
201
|
if (!options || 'object' != typeof options) throw new test_runner_namespaceObject.NodeDefinitionError('createMidsceneNodes() options must be an object.');
|
|
202
|
+
const { namespace } = options;
|
|
203
|
+
if (void 0 !== namespace && ('string' != typeof namespace || namespace.split('.').some((part)=>!part || /\s/.test(part)))) throw new test_runner_namespaceObject.NodeDefinitionError('createMidsceneNodes() namespace must be a non-empty dot-separated string without whitespace.');
|
|
202
204
|
if ('function' != typeof options.agentProvider?.getAgent && 'function' != typeof options.getAgent) throw new test_runner_namespaceObject.NodeDefinitionError('createMidsceneNodes() requires getAgent or agentProvider.getAgent.');
|
|
203
205
|
if (!options.agentClass || 'function' != typeof options.agentClass.getTestRunnerNodeDefinitions) throw new test_runner_namespaceObject.NodeDefinitionError('createMidsceneNodes() requires agentClass.getTestRunnerNodeDefinitions().');
|
|
204
206
|
const agentsByScope = new Map();
|
|
@@ -230,11 +232,16 @@ function createMidsceneNodes(options) {
|
|
|
230
232
|
return agent;
|
|
231
233
|
}), ctx.signal, 'acquisition');
|
|
232
234
|
};
|
|
233
|
-
const
|
|
235
|
+
const nodeName = (name)=>void 0 === namespace ? name : `${namespace}.${name}`;
|
|
236
|
+
const definitions = options.agentClass.getTestRunnerNodeDefinitions();
|
|
237
|
+
const agentDefinitions = void 0 === namespace ? definitions : definitions.map((definition)=>({
|
|
238
|
+
...definition,
|
|
239
|
+
name: nodeName(definition.name)
|
|
240
|
+
}));
|
|
234
241
|
return [
|
|
235
242
|
...createAgentTestRunnerNodes(agentDefinitions, getAgent),
|
|
236
243
|
defineNode({
|
|
237
|
-
name: 'wait',
|
|
244
|
+
name: nodeName('wait'),
|
|
238
245
|
description: 'Wait for a fixed duration while honoring cancellation.',
|
|
239
246
|
stringInputKey: false,
|
|
240
247
|
inputSchema: waitInputSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"midscene/index.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/node/define-node.ts","../../../src/midscene/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 { defineNode as coreDefineNode } from '@midscene/core/internal/test-runner';\nimport type {\n DefineNodeOptions,\n DefineNodeWithSchemaOptions,\n NodeDefinition,\n NodeDefinitionWithSchema,\n NodeInputSchema,\n} from './types';\n\n// A type-only facade: validation and execution still belong to the shared kernel.\ninterface DefineNativeNode {\n <TSchema extends NodeInputSchema, TData = unknown, TContext = unknown>(\n options: DefineNodeWithSchemaOptions<TSchema, TData, TContext>,\n ): NodeDefinitionWithSchema<TSchema, TData, TContext>;\n <TInput = unknown, TData = unknown, TContext = unknown>(\n options: DefineNodeOptions<TInput, TData, TContext>,\n ): NodeDefinition<TInput, TData, TContext>;\n}\nexport const defineNode: DefineNativeNode = coreDefineNode;\n","import type { IExecutionDump } from '@midscene/core';\nimport {\n type AgentTestRunnerNodeDefinition,\n type AgentTestRunnerNodeProvider,\n type CommonAgentTestRunnerApi,\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiTapInputSchema,\n aiWaitForInputSchema,\n aiWaitForOptionsInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n} from '@midscene/core/agent/test';\nimport {\n type WorkflowReportSource,\n admitResourceOperation,\n collectTestRunReportMetrics,\n hasPendingResourceOperation,\n retainResourceForScope,\n trackResourceOperation,\n} from '@midscene/core/internal/test-runner';\nimport {\n enterYamlAction,\n enterYamlExecution,\n runInYamlExecutionContext,\n} from '@midscene/core/internal/yaml-runtime';\nimport { getDebug } from '@midscene/shared/logger';\nimport { z } from 'zod/v4';\nimport type { Awaitable } from '../engine/types';\nimport { NodeDefinitionError } from '../errors';\nimport { defineNode } from '../node/define-node';\nimport type { NodeDefinition, NodeExecutionContext } from '../node/types';\n\nexport type MidsceneUIAgent = CommonAgentTestRunnerApi & {\n _prepareForTestRunner?(): void;\n _createReportSource?(\n scopeId: string,\n executionIds?: readonly string[],\n ): Promise<WorkflowReportSource | undefined>;\n flushReport?(): Promise<string | undefined>;\n addDumpUpdateListener?(\n listener: (dump: string, execution?: MidsceneExecutionRef) => void,\n ): () => void;\n};\n\nexport {\n actionInputSchema,\n aiDragAndDropInputSchema,\n aiInputInputSchema,\n aiKeyboardPressInputSchema,\n aiLongPressInputSchema,\n aiPinchInputSchema,\n aiQueryInputSchema,\n aiScrollInputSchema,\n javascriptInputSchema,\n runGherkinScenarioInputSchema,\n setAIContextInputSchema,\n} from '@midscene/core/agent/test';\nexport type {\n ActionNodeInput,\n AiDragAndDropNodeInput,\n AiInputNodeInput,\n AiKeyboardPressNodeInput,\n AiLongPressNodeInput,\n AiPinchNodeInput,\n AiQueryNodeInput,\n AiScrollNodeInput,\n JavascriptNodeInput,\n RunGherkinScenarioNodeInput,\n SetAIContextNodeInput,\n} from '@midscene/core/agent/test';\n\nexport {\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiWaitForInputSchema,\n aiWaitForOptionsInputSchema,\n aiTapInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n};\n\nexport interface MidsceneExecutionRef {\n id?: string;\n tasks?: IExecutionDump['tasks'];\n}\n\nexport interface AgentProvider<TContext> {\n getAgent(\n runId: string,\n execution: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n // biome-ignore lint/suspicious/noConfusingVoidType: providers without a report intentionally return void.\n releaseAgent?(runId: string): Awaitable<AgentReleaseResult | void>;\n dispose?(): Awaitable<void>;\n}\n\nexport interface AgentReleaseResult {\n /** Absolute path to the finalized report for this Agent scope. */\n reportPath?: string;\n}\n\nexport const waitInputSchema = z.strictObject({\n duration: z.number().positive().describe('How long to wait.'),\n unit: z\n .enum(['ms', 's', 'min'])\n .default('ms')\n .describe('Duration unit: milliseconds, seconds, or minutes.'),\n});\n\nexport type AiActNodeInput = z.infer<typeof aiActInputSchema>;\nexport type AiAssertNodeInput = z.infer<typeof aiAssertInputSchema>;\nexport type AiWaitForNodeInput = z.infer<typeof aiWaitForInputSchema>;\nexport type AiWaitForNodeOptions = z.infer<typeof aiWaitForOptionsInputSchema>;\nexport type AiTapNodeInput = z.infer<typeof aiTapInputSchema>;\nexport type InsightNodeInput = z.infer<typeof insightInputSchema>;\nexport type RecordToReportNodeInput = z.infer<typeof recordToReportInputSchema>;\nexport type WaitNodeInput = z.infer<typeof waitInputSchema>;\n\nexport interface CreateMidsceneNodesOptions<TContext> {\n getAgent?(\n execution: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n agentProvider?: AgentProvider<TContext>;\n /** Agent class that declares the Agent-backed Nodes to register. */\n agentClass: AgentTestRunnerNodeProvider;\n}\n\nconst waitFor = async (durationMs: number, signal: AbortSignal) => {\n if (signal.aborted) {\n throw signal.reason ?? new Error('Wait aborted.');\n }\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n signal.removeEventListener('abort', abort);\n resolve();\n }, durationMs);\n const abort = () => {\n clearTimeout(timeout);\n reject(signal.reason ?? new Error('Wait aborted.'));\n };\n signal.addEventListener('abort', abort, { once: true });\n });\n};\n\n/** Adapt Agent-owned descriptions to executable Midscene Test Nodes. */\nexport const createAgentTestRunnerNodes = <TContext>(\n definitions: readonly AgentTestRunnerNodeDefinition[],\n getAgent: (\n ctx: NodeExecutionContext<unknown, TContext>,\n ) => Awaitable<unknown>,\n): readonly NodeDefinition<any, any, TContext>[] => {\n const runAgentCall = async <T>(\n node: string,\n ctx: NodeExecutionContext<unknown, TContext>,\n agent: MidsceneUIAgent,\n call: () => Promise<T>,\n captureExecutions = true,\n ): Promise<T> =>\n runInYamlExecutionContext(async () => {\n const scopeId =\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n const isCleanupOwner = admitResourceOperation(agent, ctx.signal);\n if (captureExecutions) {\n ctx.report.onSnapshot(async (signal, traces) => {\n signal.throwIfAborted();\n const partial = hasPendingResourceOperation(agent);\n const capturedAt = new Date().toISOString();\n let source = await agent._createReportSource?.(\n scopeId,\n traces.map((trace) => trace.executionId),\n );\n // External adapters may only expose HTML. Capture it independently\n // of cleanup, while marking its mutable source as partial.\n if (!source) {\n const path = await agent.flushReport?.();\n if (path)\n source = {\n sourcePath: path,\n collection: {\n status: 'partial',\n capturedAt,\n reason:\n 'This provider exposes live HTML without immutable snapshot support.',\n },\n };\n }\n signal.throwIfAborted();\n if (!source)\n throw new Error('Agent did not return a report snapshot.');\n return {\n ...source,\n collection: {\n status:\n partial || source.collection?.status === 'partial'\n ? ('partial' as const)\n : ('complete' as const),\n capturedAt: source.collection?.capturedAt ?? capturedAt,\n ...(source.collection?.reason\n ? { reason: source.collection.reason }\n : partial\n ? {\n reason:\n 'An Agent operation was still running at the snapshot cutoff.',\n }\n : {}),\n },\n };\n });\n }\n // Native Steps own the root report. Any Agent.runYaml invoked underneath\n // contributes to that Step instead of publishing another root snapshot.\n const yamlBoundary = enterYamlExecution(agent, {\n allowIndependentOverlap: isCleanupOwner,\n });\n const leaveYamlAction = enterYamlAction(agent, ctx.signal);\n let removeListener: (() => void) | undefined;\n let sawExecutionWithoutId = false;\n const stopListening = () => {\n const remove = removeListener;\n removeListener = undefined;\n remove?.();\n };\n const stopListeningOnAbort = () => stopListening();\n try {\n if (captureExecutions && agent.addDumpUpdateListener) {\n removeListener = agent.addDumpUpdateListener((_dump, execution) => {\n if (!execution) return;\n if (!execution.id) {\n sawExecutionWithoutId = true;\n return;\n }\n ctx.report.addTrace({\n type: 'midscene-execution',\n executionId: execution.id,\n ...(execution.tasks\n ? {\n metrics: collectTestRunReportMetrics({\n executions: [\n {\n id: execution.id,\n name: node,\n logTime: 0,\n tasks: execution.tasks,\n },\n ],\n }),\n }\n : {}),\n });\n });\n if (ctx.signal.aborted) stopListening();\n else {\n ctx.signal.addEventListener('abort', stopListeningOnAbort, {\n once: true,\n });\n }\n }\n return await trackResourceOperation(\n agent,\n Promise.resolve().then(call),\n ctx.signal,\n );\n } finally {\n leaveYamlAction();\n yamlBoundary.finish();\n ctx.signal.removeEventListener('abort', stopListeningOnAbort);\n stopListening();\n if (sawExecutionWithoutId) {\n warnReportTrace(\n `Skipped a report trace without a stable execution id for ${node} in scope ${scopeId}.`,\n );\n }\n }\n });\n\n return definitions.map((definition) =>\n defineNode({\n name: definition.name,\n ...(definition.title === undefined ? {} : { title: definition.title }),\n ...(definition.description === undefined\n ? {}\n : { description: definition.description }),\n ...(definition.stringInputKey === undefined\n ? {}\n : { stringInputKey: definition.stringInputKey }),\n inputSchema: definition.inputSchema,\n async execute(ctx) {\n const agent = await getAgent(ctx);\n ctx.signal.throwIfAborted();\n const call = async () =>\n definition.execute(agent, ctx.input, { signal: ctx.signal });\n if (typeof agent !== 'object' || agent === null) return call();\n return runAgentCall(\n definition.name,\n ctx,\n agent as MidsceneUIAgent,\n call,\n );\n },\n }),\n );\n};\n\nconst warnReportTrace = getDebug('test-runner:report-trace', {\n console: true,\n});\n\nexport function createMidsceneNodes<TContext>(\n options: CreateMidsceneNodesOptions<TContext>,\n): readonly NodeDefinition<any, any, TContext>[] {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() options must be an object.',\n );\n }\n if (\n typeof options.agentProvider?.getAgent !== 'function' &&\n typeof options.getAgent !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires getAgent or agentProvider.getAgent.',\n );\n }\n if (\n !options.agentClass ||\n typeof options.agentClass.getTestRunnerNodeDefinitions !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires agentClass.getTestRunnerNodeDefinitions().',\n );\n }\n\n const agentsByScope = new Map<string, Set<MidsceneUIAgent>>();\n const getAgent = async (\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Promise<MidsceneUIAgent> => {\n const runId =\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n let agents = agentsByScope.get(runId);\n if (!agents) {\n agents = new Set();\n agentsByScope.set(runId, agents);\n ctx.onTeardown(async () => {\n try {\n const released = await options.agentProvider?.releaseAgent?.(runId);\n return released?.reportPath\n ? { reportPaths: [released.reportPath] }\n : undefined;\n } finally {\n agentsByScope.delete(runId);\n }\n });\n }\n const scopeAgents = agents;\n // Acquisition can outlive cancellation. Teardown waits for actual settlement.\n return trackResourceOperation(\n {},\n Promise.resolve().then(async () => {\n const agent = options.agentProvider\n ? await options.agentProvider.getAgent(runId, ctx)\n : await options.getAgent!(ctx);\n retainResourceForScope(agent, ctx.signal);\n agent._prepareForTestRunner?.();\n scopeAgents.add(agent);\n return agent;\n }),\n ctx.signal,\n 'acquisition',\n );\n };\n\n const agentDefinitions = options.agentClass.getTestRunnerNodeDefinitions();\n\n return [\n ...createAgentTestRunnerNodes(agentDefinitions, getAgent),\n defineNode<typeof waitInputSchema, unknown, TContext>({\n name: 'wait',\n description: 'Wait for a fixed duration while honoring cancellation.',\n stringInputKey: false,\n inputSchema: waitInputSchema,\n async execute(execution) {\n const multiplier =\n execution.input.unit === 'min'\n ? 60_000\n : execution.input.unit === 's'\n ? 1_000\n : 1;\n const durationMs = execution.input.duration * multiplier;\n await waitFor(durationMs, execution.signal);\n return { summary: `Waited ${durationMs}ms` };\n },\n }),\n ];\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","defineNode","coreDefineNode","waitInputSchema","z","waitFor","durationMs","signal","Error","Promise","resolve","reject","timeout","setTimeout","abort","clearTimeout","createAgentTestRunnerNodes","definitions","getAgent","runAgentCall","node","ctx","agent","call","captureExecutions","runInYamlExecutionContext","scopeId","isCleanupOwner","admitResourceOperation","traces","partial","hasPendingResourceOperation","capturedAt","Date","source","trace","path","yamlBoundary","enterYamlExecution","leaveYamlAction","enterYamlAction","removeListener","sawExecutionWithoutId","stopListening","remove","undefined","stopListeningOnAbort","_dump","execution","collectTestRunReportMetrics","trackResourceOperation","warnReportTrace","getDebug","createMidsceneNodes","options","NodeDefinitionError","agentsByScope","Map","runId","agents","Set","released","scopeAgents","retainResourceForScope","agentDefinitions","multiplier"],"mappings":";;;IAAAA,oBAAoB,CAAC,GAAG,CAAC,UAASC;QACjC,IAAI,IAAIC,OAAOD,WACR,IAAGD,oBAAoB,CAAC,CAACC,YAAYC,QAAQ,CAACF,oBAAoB,CAAC,CAAC,UAASE,MACzEC,OAAO,cAAc,CAAC,UAASD,KAAK;YAAE,YAAY;YAAM,KAAKD,UAAU,CAACC,IAAI;QAAC;IAGzF;;;ICNAF,oBAAoB,CAAC,GAAG,CAACI,KAAKC,OAAUF,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACC,KAAKC;;;ICClFL,oBAAoB,CAAC,GAAG,CAAC;QACxB,IAAG,AAAkB,eAAlB,OAAOM,UAA0BA,OAAO,WAAW,EACrDH,OAAO,cAAc,CAAC,UAASG,OAAO,WAAW,EAAE;YAAE,OAAO;QAAS;QAEtEH,OAAO,cAAc,CAAC,UAAS,cAAc;YAAE,OAAO;QAAK;IAC5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACYO,MAAMI,aAA+BC,4BAAAA,UAAcA;ACsGnD,MAAMC,kBAAkBC,mBAAAA,CAAAA,CAAAA,YAAc,CAAC;IAC5C,UAAUA,mBAAAA,CAAAA,CAAAA,MAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACzC,MAAMA,mBAAAA,CAAAA,CAAAA,OACC,CAAC;QAAC;QAAM;QAAK;KAAM,EACvB,OAAO,CAAC,MACR,QAAQ,CAAC;AACd;AAoBA,MAAMC,UAAU,OAAOC,YAAoBC;IACzC,IAAIA,OAAO,OAAO,EAChB,MAAMA,OAAO,MAAM,IAAI,IAAIC,MAAM;IAEnC,MAAM,IAAIC,QAAc,CAACC,SAASC;QAChC,MAAMC,UAAUC,WAAW;YACzBN,OAAO,mBAAmB,CAAC,SAASO;YACpCJ;QACF,GAAGJ;QACH,MAAMQ,QAAQ;YACZC,aAAaH;YACbD,OAAOJ,OAAO,MAAM,IAAI,IAAIC,MAAM;QACpC;QACAD,OAAO,gBAAgB,CAAC,SAASO,OAAO;YAAE,MAAM;QAAK;IACvD;AACF;AAGO,MAAME,6BAA6B,CACxCC,aACAC;IAIA,MAAMC,eAAe,OACnBC,MACAC,KACAC,OACAC,MACAC,oBAAoB,IAAI,GAExBC,AAAAA,IAAAA,6BAAAA,yBAAAA,AAAAA,EAA0B;YACxB,MAAMC,UACJL,AAAc,WAAdA,IAAI,KAAK,GAAcA,IAAI,IAAI,CAAC,KAAK,GAAGA,IAAI,QAAQ,CAAC,aAAa;YACpE,MAAMM,iBAAiBC,AAAAA,IAAAA,4BAAAA,sBAAAA,AAAAA,EAAuBN,OAAOD,IAAI,MAAM;YAC/D,IAAIG,mBACFH,IAAI,MAAM,CAAC,UAAU,CAAC,OAAOd,QAAQsB;gBACnCtB,OAAO,cAAc;gBACrB,MAAMuB,UAAUC,AAAAA,IAAAA,4BAAAA,2BAAAA,AAAAA,EAA4BT;gBAC5C,MAAMU,aAAa,IAAIC,OAAO,WAAW;gBACzC,IAAIC,SAAS,MAAMZ,MAAM,mBAAmB,GAC1CI,SACAG,OAAO,GAAG,CAAC,CAACM,QAAUA,MAAM,WAAW;gBAIzC,IAAI,CAACD,QAAQ;oBACX,MAAME,OAAO,MAAMd,MAAM,WAAW;oBACpC,IAAIc,MACFF,SAAS;wBACP,YAAYE;wBACZ,YAAY;4BACV,QAAQ;4BACRJ;4BACA,QACE;wBACJ;oBACF;gBACJ;gBACAzB,OAAO,cAAc;gBACrB,IAAI,CAAC2B,QACH,MAAM,IAAI1B,MAAM;gBAClB,OAAO;oBACL,GAAG0B,MAAM;oBACT,YAAY;wBACV,QACEJ,WAAWI,OAAO,UAAU,EAAE,WAAW,YACpC,YACA;wBACP,YAAYA,OAAO,UAAU,EAAE,cAAcF;wBAC7C,GAAIE,OAAO,UAAU,EAAE,SACnB;4BAAE,QAAQA,OAAO,UAAU,CAAC,MAAM;wBAAC,IACnCJ,UACE;4BACE,QACE;wBACJ,IACA,CAAC,CAAC;oBACV;gBACF;YACF;YAIF,MAAMO,eAAeC,AAAAA,IAAAA,6BAAAA,kBAAAA,AAAAA,EAAmBhB,OAAO;gBAC7C,yBAAyBK;YAC3B;YACA,MAAMY,kBAAkBC,AAAAA,IAAAA,6BAAAA,eAAAA,AAAAA,EAAgBlB,OAAOD,IAAI,MAAM;YACzD,IAAIoB;YACJ,IAAIC,wBAAwB;YAC5B,MAAMC,gBAAgB;gBACpB,MAAMC,SAASH;gBACfA,iBAAiBI;gBACjBD;YACF;YACA,MAAME,uBAAuB,IAAMH;YACnC,IAAI;gBACF,IAAInB,qBAAqBF,MAAM,qBAAqB,EAAE;oBACpDmB,iBAAiBnB,MAAM,qBAAqB,CAAC,CAACyB,OAAOC;wBACnD,IAAI,CAACA,WAAW;wBAChB,IAAI,CAACA,UAAU,EAAE,EAAE;4BACjBN,wBAAwB;4BACxB;wBACF;wBACArB,IAAI,MAAM,CAAC,QAAQ,CAAC;4BAClB,MAAM;4BACN,aAAa2B,UAAU,EAAE;4BACzB,GAAIA,UAAU,KAAK,GACf;gCACE,SAASC,AAAAA,IAAAA,4BAAAA,2BAAAA,AAAAA,EAA4B;oCACnC,YAAY;wCACV;4CACE,IAAID,UAAU,EAAE;4CAChB,MAAM5B;4CACN,SAAS;4CACT,OAAO4B,UAAU,KAAK;wCACxB;qCACD;gCACH;4BACF,IACA,CAAC,CAAC;wBACR;oBACF;oBACA,IAAI3B,IAAI,MAAM,CAAC,OAAO,EAAEsB;yBAEtBtB,IAAI,MAAM,CAAC,gBAAgB,CAAC,SAASyB,sBAAsB;wBACzD,MAAM;oBACR;gBAEJ;gBACA,OAAO,MAAMI,AAAAA,IAAAA,4BAAAA,sBAAAA,AAAAA,EACX5B,OACAb,QAAQ,OAAO,GAAG,IAAI,CAACc,OACvBF,IAAI,MAAM;YAEd,SAAU;gBACRkB;gBACAF,aAAa,MAAM;gBACnBhB,IAAI,MAAM,CAAC,mBAAmB,CAAC,SAASyB;gBACxCH;gBACA,IAAID,uBACFS,gBACE,CAAC,yDAAyD,EAAE/B,KAAK,UAAU,EAAEM,QAAQ,CAAC,CAAC;YAG7F;QACF;IAEF,OAAOT,YAAY,GAAG,CAAC,CAACtB,aACtBM,WAAW;YACT,MAAMN,WAAW,IAAI;YACrB,GAAIA,AAAqBkD,WAArBlD,WAAW,KAAK,GAAiB,CAAC,IAAI;gBAAE,OAAOA,WAAW,KAAK;YAAC,CAAC;YACrE,GAAIA,AAA2BkD,WAA3BlD,WAAW,WAAW,GACtB,CAAC,IACD;gBAAE,aAAaA,WAAW,WAAW;YAAC,CAAC;YAC3C,GAAIA,AAA8BkD,WAA9BlD,WAAW,cAAc,GACzB,CAAC,IACD;gBAAE,gBAAgBA,WAAW,cAAc;YAAC,CAAC;YACjD,aAAaA,WAAW,WAAW;YACnC,MAAM,SAAQ0B,GAAG;gBACf,MAAMC,QAAQ,MAAMJ,SAASG;gBAC7BA,IAAI,MAAM,CAAC,cAAc;gBACzB,MAAME,OAAO,UACX5B,WAAW,OAAO,CAAC2B,OAAOD,IAAI,KAAK,EAAE;wBAAE,QAAQA,IAAI,MAAM;oBAAC;gBAC5D,IAAI,AAAiB,YAAjB,OAAOC,SAAsBA,AAAU,SAAVA,OAAgB,OAAOC;gBACxD,OAAOJ,aACLxB,WAAW,IAAI,EACf0B,KACAC,OACAC;YAEJ;QACF;AAEJ;AAEA,MAAM4B,kBAAkBC,AAAAA,IAAAA,uBAAAA,QAAAA,AAAAA,EAAS,4BAA4B;IAC3D,SAAS;AACX;AAEO,SAASC,oBACdC,OAA6C;IAE7C,IAAI,CAACA,WAAW,AAAmB,YAAnB,OAAOA,SACrB,MAAM,IAAIC,4BAAAA,mBAAmBA,CAC3B;IAGJ,IACE,AAA2C,cAA3C,OAAOD,QAAQ,aAAa,EAAE,YAC9B,AAA4B,cAA5B,OAAOA,QAAQ,QAAQ,EAEvB,MAAM,IAAIC,4BAAAA,mBAAmBA,CAC3B;IAGJ,IACE,CAACD,QAAQ,UAAU,IACnB,AAA2D,cAA3D,OAAOA,QAAQ,UAAU,CAAC,4BAA4B,EAEtD,MAAM,IAAIC,4BAAAA,mBAAmBA,CAC3B;IAIJ,MAAMC,gBAAgB,IAAIC;IAC1B,MAAMvC,WAAW,OACfG;QAEA,MAAMqC,QACJrC,AAAc,WAAdA,IAAI,KAAK,GAAcA,IAAI,IAAI,CAAC,KAAK,GAAGA,IAAI,QAAQ,CAAC,aAAa;QACpE,IAAIsC,SAASH,cAAc,GAAG,CAACE;QAC/B,IAAI,CAACC,QAAQ;YACXA,SAAS,IAAIC;YACbJ,cAAc,GAAG,CAACE,OAAOC;YACzBtC,IAAI,UAAU,CAAC;gBACb,IAAI;oBACF,MAAMwC,WAAW,MAAMP,QAAQ,aAAa,EAAE,eAAeI;oBAC7D,OAAOG,UAAU,aACb;wBAAE,aAAa;4BAACA,SAAS,UAAU;yBAAC;oBAAC,IACrChB;gBACN,SAAU;oBACRW,cAAc,MAAM,CAACE;gBACvB;YACF;QACF;QACA,MAAMI,cAAcH;QAEpB,OAAOT,AAAAA,IAAAA,4BAAAA,sBAAAA,AAAAA,EACL,CAAC,GACDzC,QAAQ,OAAO,GAAG,IAAI,CAAC;YACrB,MAAMa,QAAQgC,QAAQ,aAAa,GAC/B,MAAMA,QAAQ,aAAa,CAAC,QAAQ,CAACI,OAAOrC,OAC5C,MAAMiC,QAAQ,QAAQ,CAAEjC;YAC5B0C,IAAAA,4BAAAA,sBAAAA,AAAAA,EAAuBzC,OAAOD,IAAI,MAAM;YACxCC,MAAM,qBAAqB;YAC3BwC,YAAY,GAAG,CAACxC;YAChB,OAAOA;QACT,IACAD,IAAI,MAAM,EACV;IAEJ;IAEA,MAAM2C,mBAAmBV,QAAQ,UAAU,CAAC,4BAA4B;IAExE,OAAO;WACFtC,2BAA2BgD,kBAAkB9C;QAChDjB,WAAsD;YACpD,MAAM;YACN,aAAa;YACb,gBAAgB;YAChB,aAAaE;YACb,MAAM,SAAQ6C,SAAS;gBACrB,MAAMiB,aACJjB,AAAyB,UAAzBA,UAAU,KAAK,CAAC,IAAI,GAChB,QACAA,AAAyB,QAAzBA,UAAU,KAAK,CAAC,IAAI,GAClB,OACA;gBACR,MAAM1C,aAAa0C,UAAU,KAAK,CAAC,QAAQ,GAAGiB;gBAC9C,MAAM5D,QAAQC,YAAY0C,UAAU,MAAM;gBAC1C,OAAO;oBAAE,SAAS,CAAC,OAAO,EAAE1C,WAAW,EAAE,CAAC;gBAAC;YAC7C;QACF;KACD;AACH"}
|
|
1
|
+
{"version":3,"file":"midscene/index.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/node/define-node.ts","../../../src/midscene/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 { defineNode as coreDefineNode } from '@midscene/core/internal/test-runner';\nimport type {\n DefineNodeOptions,\n DefineNodeWithSchemaOptions,\n NodeDefinition,\n NodeDefinitionWithSchema,\n NodeInputSchema,\n} from './types';\n\n// A type-only facade: validation and execution still belong to the shared kernel.\ninterface DefineNativeNode {\n <TSchema extends NodeInputSchema, TData = unknown, TContext = unknown>(\n options: DefineNodeWithSchemaOptions<TSchema, TData, TContext>,\n ): NodeDefinitionWithSchema<TSchema, TData, TContext>;\n <TInput = unknown, TData = unknown, TContext = unknown>(\n options: DefineNodeOptions<TInput, TData, TContext>,\n ): NodeDefinition<TInput, TData, TContext>;\n}\nexport const defineNode: DefineNativeNode = coreDefineNode;\n","import type { IExecutionDump } from '@midscene/core';\nimport {\n type AgentTestRunnerNodeDefinition,\n type AgentTestRunnerNodeProvider,\n type CommonAgentTestRunnerApi,\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiTapInputSchema,\n aiWaitForInputSchema,\n aiWaitForOptionsInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n} from '@midscene/core/agent/test';\nimport {\n type WorkflowReportSource,\n admitResourceOperation,\n collectTestRunReportMetrics,\n hasPendingResourceOperation,\n retainResourceForScope,\n trackResourceOperation,\n} from '@midscene/core/internal/test-runner';\nimport {\n enterYamlAction,\n enterYamlExecution,\n runInYamlExecutionContext,\n} from '@midscene/core/internal/yaml-runtime';\nimport { getDebug } from '@midscene/shared/logger';\nimport { z } from 'zod/v4';\nimport type { Awaitable } from '../engine/types';\nimport { NodeDefinitionError } from '../errors';\nimport { defineNode } from '../node/define-node';\nimport type { NodeDefinition, NodeExecutionContext } from '../node/types';\n\nexport type MidsceneUIAgent = CommonAgentTestRunnerApi & {\n _prepareForTestRunner?(): void;\n _createReportSource?(\n scopeId: string,\n executionIds?: readonly string[],\n ): Promise<WorkflowReportSource | undefined>;\n flushReport?(): Promise<string | undefined>;\n addDumpUpdateListener?(\n listener: (dump: string, execution?: MidsceneExecutionRef) => void,\n ): () => void;\n};\n\nexport {\n actionInputSchema,\n aiDragAndDropInputSchema,\n aiInputInputSchema,\n aiKeyboardPressInputSchema,\n aiLongPressInputSchema,\n aiPinchInputSchema,\n aiQueryInputSchema,\n aiScrollInputSchema,\n javascriptInputSchema,\n runGherkinScenarioInputSchema,\n setAIContextInputSchema,\n} from '@midscene/core/agent/test';\nexport type {\n ActionNodeInput,\n AiDragAndDropNodeInput,\n AiInputNodeInput,\n AiKeyboardPressNodeInput,\n AiLongPressNodeInput,\n AiPinchNodeInput,\n AiQueryNodeInput,\n AiScrollNodeInput,\n JavascriptNodeInput,\n RunGherkinScenarioNodeInput,\n SetAIContextNodeInput,\n} from '@midscene/core/agent/test';\n\nexport {\n aiActInputSchema,\n aiActOptionsInputSchema,\n aiAssertInputSchema,\n aiAssertOptionsInputSchema,\n aiWaitForInputSchema,\n aiWaitForOptionsInputSchema,\n aiTapInputSchema,\n insightInputSchema,\n insightOptionsInputSchema,\n locateOptionsInputSchema,\n promptImageInputSchema,\n recordToReportInputSchema,\n recordToReportOptionsInputSchema,\n reportScreenshotInputSchema,\n structuredUserPromptInputSchema,\n userPromptInputSchema,\n};\n\nexport interface MidsceneExecutionRef {\n id?: string;\n tasks?: IExecutionDump['tasks'];\n}\n\nexport interface AgentProvider<TContext> {\n getAgent(\n runId: string,\n execution: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n // biome-ignore lint/suspicious/noConfusingVoidType: providers without a report intentionally return void.\n releaseAgent?(runId: string): Awaitable<AgentReleaseResult | void>;\n dispose?(): Awaitable<void>;\n}\n\nexport interface AgentReleaseResult {\n /** Absolute path to the finalized report for this Agent scope. */\n reportPath?: string;\n}\n\nexport const waitInputSchema = z.strictObject({\n duration: z.number().positive().describe('How long to wait.'),\n unit: z\n .enum(['ms', 's', 'min'])\n .default('ms')\n .describe('Duration unit: milliseconds, seconds, or minutes.'),\n});\n\nexport type AiActNodeInput = z.infer<typeof aiActInputSchema>;\nexport type AiAssertNodeInput = z.infer<typeof aiAssertInputSchema>;\nexport type AiWaitForNodeInput = z.infer<typeof aiWaitForInputSchema>;\nexport type AiWaitForNodeOptions = z.infer<typeof aiWaitForOptionsInputSchema>;\nexport type AiTapNodeInput = z.infer<typeof aiTapInputSchema>;\nexport type InsightNodeInput = z.infer<typeof insightInputSchema>;\nexport type RecordToReportNodeInput = z.infer<typeof recordToReportInputSchema>;\nexport type WaitNodeInput = z.infer<typeof waitInputSchema>;\n\nexport interface CreateMidsceneNodesOptions<TContext> {\n /**\n * Prefix every returned Node name, including wait, with `${namespace}.`.\n * Use non-empty dot-separated segments without whitespace.\n */\n namespace?: string;\n getAgent?(\n execution: NodeExecutionContext<unknown, TContext>,\n ): Awaitable<MidsceneUIAgent>;\n agentProvider?: AgentProvider<TContext>;\n /** Agent class that declares the Agent-backed Nodes to register. */\n agentClass: AgentTestRunnerNodeProvider;\n}\n\nconst waitFor = async (durationMs: number, signal: AbortSignal) => {\n if (signal.aborted) {\n throw signal.reason ?? new Error('Wait aborted.');\n }\n await new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(() => {\n signal.removeEventListener('abort', abort);\n resolve();\n }, durationMs);\n const abort = () => {\n clearTimeout(timeout);\n reject(signal.reason ?? new Error('Wait aborted.'));\n };\n signal.addEventListener('abort', abort, { once: true });\n });\n};\n\n/** Adapt Agent-owned descriptions to executable Midscene Test Nodes. */\nexport const createAgentTestRunnerNodes = <TContext>(\n definitions: readonly AgentTestRunnerNodeDefinition[],\n getAgent: (\n ctx: NodeExecutionContext<unknown, TContext>,\n ) => Awaitable<unknown>,\n): readonly NodeDefinition<any, any, TContext>[] => {\n const runAgentCall = async <T>(\n node: string,\n ctx: NodeExecutionContext<unknown, TContext>,\n agent: MidsceneUIAgent,\n call: () => Promise<T>,\n captureExecutions = true,\n ): Promise<T> =>\n runInYamlExecutionContext(async () => {\n const scopeId =\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n const isCleanupOwner = admitResourceOperation(agent, ctx.signal);\n if (captureExecutions) {\n ctx.report.onSnapshot(async (signal, traces) => {\n signal.throwIfAborted();\n const partial = hasPendingResourceOperation(agent);\n const capturedAt = new Date().toISOString();\n let source = await agent._createReportSource?.(\n scopeId,\n traces.map((trace) => trace.executionId),\n );\n // External adapters may only expose HTML. Capture it independently\n // of cleanup, while marking its mutable source as partial.\n if (!source) {\n const path = await agent.flushReport?.();\n if (path)\n source = {\n sourcePath: path,\n collection: {\n status: 'partial',\n capturedAt,\n reason:\n 'This provider exposes live HTML without immutable snapshot support.',\n },\n };\n }\n signal.throwIfAborted();\n if (!source)\n throw new Error('Agent did not return a report snapshot.');\n return {\n ...source,\n collection: {\n status:\n partial || source.collection?.status === 'partial'\n ? ('partial' as const)\n : ('complete' as const),\n capturedAt: source.collection?.capturedAt ?? capturedAt,\n ...(source.collection?.reason\n ? { reason: source.collection.reason }\n : partial\n ? {\n reason:\n 'An Agent operation was still running at the snapshot cutoff.',\n }\n : {}),\n },\n };\n });\n }\n // Native Steps own the root report. Any Agent.runYaml invoked underneath\n // contributes to that Step instead of publishing another root snapshot.\n const yamlBoundary = enterYamlExecution(agent, {\n allowIndependentOverlap: isCleanupOwner,\n });\n const leaveYamlAction = enterYamlAction(agent, ctx.signal);\n let removeListener: (() => void) | undefined;\n let sawExecutionWithoutId = false;\n const stopListening = () => {\n const remove = removeListener;\n removeListener = undefined;\n remove?.();\n };\n const stopListeningOnAbort = () => stopListening();\n try {\n if (captureExecutions && agent.addDumpUpdateListener) {\n removeListener = agent.addDumpUpdateListener((_dump, execution) => {\n if (!execution) return;\n if (!execution.id) {\n sawExecutionWithoutId = true;\n return;\n }\n ctx.report.addTrace({\n type: 'midscene-execution',\n executionId: execution.id,\n ...(execution.tasks\n ? {\n metrics: collectTestRunReportMetrics({\n executions: [\n {\n id: execution.id,\n name: node,\n logTime: 0,\n tasks: execution.tasks,\n },\n ],\n }),\n }\n : {}),\n });\n });\n if (ctx.signal.aborted) stopListening();\n else {\n ctx.signal.addEventListener('abort', stopListeningOnAbort, {\n once: true,\n });\n }\n }\n return await trackResourceOperation(\n agent,\n Promise.resolve().then(call),\n ctx.signal,\n );\n } finally {\n leaveYamlAction();\n yamlBoundary.finish();\n ctx.signal.removeEventListener('abort', stopListeningOnAbort);\n stopListening();\n if (sawExecutionWithoutId) {\n warnReportTrace(\n `Skipped a report trace without a stable execution id for ${node} in scope ${scopeId}.`,\n );\n }\n }\n });\n\n return definitions.map((definition) =>\n defineNode({\n name: definition.name,\n ...(definition.title === undefined ? {} : { title: definition.title }),\n ...(definition.description === undefined\n ? {}\n : { description: definition.description }),\n ...(definition.stringInputKey === undefined\n ? {}\n : { stringInputKey: definition.stringInputKey }),\n inputSchema: definition.inputSchema,\n async execute(ctx) {\n const agent = await getAgent(ctx);\n ctx.signal.throwIfAborted();\n const call = async () =>\n definition.execute(agent, ctx.input, { signal: ctx.signal });\n if (typeof agent !== 'object' || agent === null) return call();\n return runAgentCall(\n definition.name,\n ctx,\n agent as MidsceneUIAgent,\n call,\n );\n },\n }),\n );\n};\n\nconst warnReportTrace = getDebug('test-runner:report-trace', {\n console: true,\n});\n\nexport function createMidsceneNodes<TContext>(\n options: CreateMidsceneNodesOptions<TContext>,\n): readonly NodeDefinition<any, any, TContext>[] {\n if (!options || typeof options !== 'object') {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() options must be an object.',\n );\n }\n const { namespace } = options;\n if (\n namespace !== undefined &&\n (typeof namespace !== 'string' ||\n namespace.split('.').some((part) => !part || /\\s/.test(part)))\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() namespace must be a non-empty dot-separated string without whitespace.',\n );\n }\n if (\n typeof options.agentProvider?.getAgent !== 'function' &&\n typeof options.getAgent !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires getAgent or agentProvider.getAgent.',\n );\n }\n if (\n !options.agentClass ||\n typeof options.agentClass.getTestRunnerNodeDefinitions !== 'function'\n ) {\n throw new NodeDefinitionError(\n 'createMidsceneNodes() requires agentClass.getTestRunnerNodeDefinitions().',\n );\n }\n\n const agentsByScope = new Map<string, Set<MidsceneUIAgent>>();\n const getAgent = async (\n ctx: NodeExecutionContext<unknown, TContext>,\n ): Promise<MidsceneUIAgent> => {\n const runId =\n ctx.scope === 'case' ? ctx.case.runId : ctx.document.documentRunId;\n let agents = agentsByScope.get(runId);\n if (!agents) {\n agents = new Set();\n agentsByScope.set(runId, agents);\n ctx.onTeardown(async () => {\n try {\n const released = await options.agentProvider?.releaseAgent?.(runId);\n return released?.reportPath\n ? { reportPaths: [released.reportPath] }\n : undefined;\n } finally {\n agentsByScope.delete(runId);\n }\n });\n }\n const scopeAgents = agents;\n // Acquisition can outlive cancellation. Teardown waits for actual settlement.\n return trackResourceOperation(\n {},\n Promise.resolve().then(async () => {\n const agent = options.agentProvider\n ? await options.agentProvider.getAgent(runId, ctx)\n : await options.getAgent!(ctx);\n retainResourceForScope(agent, ctx.signal);\n agent._prepareForTestRunner?.();\n scopeAgents.add(agent);\n return agent;\n }),\n ctx.signal,\n 'acquisition',\n );\n };\n\n const nodeName = (name: string) =>\n namespace === undefined ? name : `${namespace}.${name}`;\n const definitions = options.agentClass.getTestRunnerNodeDefinitions();\n const agentDefinitions =\n namespace === undefined\n ? definitions\n : definitions.map((definition) => ({\n ...definition,\n name: nodeName(definition.name),\n }));\n\n return [\n ...createAgentTestRunnerNodes(agentDefinitions, getAgent),\n defineNode<typeof waitInputSchema, unknown, TContext>({\n name: nodeName('wait'),\n description: 'Wait for a fixed duration while honoring cancellation.',\n stringInputKey: false,\n inputSchema: waitInputSchema,\n async execute(execution) {\n const multiplier =\n execution.input.unit === 'min'\n ? 60_000\n : execution.input.unit === 's'\n ? 1_000\n : 1;\n const durationMs = execution.input.duration * multiplier;\n await waitFor(durationMs, execution.signal);\n return { summary: `Waited ${durationMs}ms` };\n },\n }),\n ];\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","defineNode","coreDefineNode","waitInputSchema","z","waitFor","durationMs","signal","Error","Promise","resolve","reject","timeout","setTimeout","abort","clearTimeout","createAgentTestRunnerNodes","definitions","getAgent","runAgentCall","node","ctx","agent","call","captureExecutions","runInYamlExecutionContext","scopeId","isCleanupOwner","admitResourceOperation","traces","partial","hasPendingResourceOperation","capturedAt","Date","source","trace","path","yamlBoundary","enterYamlExecution","leaveYamlAction","enterYamlAction","removeListener","sawExecutionWithoutId","stopListening","remove","undefined","stopListeningOnAbort","_dump","execution","collectTestRunReportMetrics","trackResourceOperation","warnReportTrace","getDebug","createMidsceneNodes","options","NodeDefinitionError","namespace","part","agentsByScope","Map","runId","agents","Set","released","scopeAgents","retainResourceForScope","nodeName","name","agentDefinitions","multiplier"],"mappings":"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"}
|
package/dist/lib/test-cli.js
CHANGED
|
@@ -118,6 +118,11 @@ export declare const createAgentTestRunnerNodes: <TContext>(definitions: readonl
|
|
|
118
118
|
export declare function createMidsceneNodes<TContext>(options: CreateMidsceneNodesOptions<TContext>): readonly NodeDefinition<any, any, TContext>[];
|
|
119
119
|
|
|
120
120
|
export declare interface CreateMidsceneNodesOptions<TContext> {
|
|
121
|
+
/**
|
|
122
|
+
* Prefix every returned Node name, including wait, with `${namespace}.`.
|
|
123
|
+
* Use non-empty dot-separated segments without whitespace.
|
|
124
|
+
*/
|
|
125
|
+
namespace?: string;
|
|
121
126
|
getAgent?(execution: NodeExecutionContext<unknown, TContext>): Awaitable<MidsceneUIAgent>;
|
|
122
127
|
agentProvider?: AgentProvider<TContext>;
|
|
123
128
|
/** Agent class that declares the Agent-backed Nodes to register. */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midscene/test",
|
|
3
3
|
"description": "Midscene Test: an extensible YAML testing framework and Node SDK.",
|
|
4
|
-
"version": "1.17.
|
|
4
|
+
"version": "1.17.1",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/web-infra-dev/midscene.git",
|
|
@@ -59,8 +59,8 @@
|
|
|
59
59
|
"tsx": "^4.19.2",
|
|
60
60
|
"yargs": "17.7.3",
|
|
61
61
|
"zod": "^3.25.1",
|
|
62
|
-
"@midscene/shared": "1.17.
|
|
63
|
-
"@midscene/core": "1.17.
|
|
62
|
+
"@midscene/shared": "1.17.1",
|
|
63
|
+
"@midscene/core": "1.17.1"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@rslib/core": "^0.18.3",
|
|
@@ -72,20 +72,20 @@
|
|
|
72
72
|
"puppeteer": "24.6.0",
|
|
73
73
|
"typescript": "^5.8.3",
|
|
74
74
|
"vitest": "3.0.5",
|
|
75
|
-
"@midscene/
|
|
76
|
-
"@midscene/
|
|
77
|
-
"@midscene/
|
|
78
|
-
"@midscene/
|
|
79
|
-
"@midscene/
|
|
75
|
+
"@midscene/harmony": "1.17.1",
|
|
76
|
+
"@midscene/ios": "1.17.1",
|
|
77
|
+
"@midscene/web": "1.17.1",
|
|
78
|
+
"@midscene/android": "1.17.1",
|
|
79
|
+
"@midscene/computer": "1.17.1"
|
|
80
80
|
},
|
|
81
81
|
"peerDependencies": {
|
|
82
82
|
"playwright": "^1.45.0",
|
|
83
83
|
"puppeteer": "24.6.0",
|
|
84
|
-
"@midscene/android": "1.17.
|
|
85
|
-
"@midscene/
|
|
86
|
-
"@midscene/
|
|
87
|
-
"@midscene/
|
|
88
|
-
"@midscene/
|
|
84
|
+
"@midscene/android": "1.17.1",
|
|
85
|
+
"@midscene/web": "1.17.1",
|
|
86
|
+
"@midscene/computer": "1.17.1",
|
|
87
|
+
"@midscene/ios": "1.17.1",
|
|
88
|
+
"@midscene/harmony": "1.17.1"
|
|
89
89
|
},
|
|
90
90
|
"peerDependenciesMeta": {
|
|
91
91
|
"@midscene/android": {
|