@midscene/core 1.16.0 → 1.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/agent/agent-options.mjs +36 -0
- package/dist/es/agent/agent-options.mjs.map +1 -0
- package/dist/es/agent/agent-report-manager.mjs +323 -0
- package/dist/es/agent/agent-report-manager.mjs.map +1 -0
- package/dist/es/agent/agent.mjs +112 -228
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/cache-config.mjs +21 -1
- package/dist/es/agent/cache-config.mjs.map +1 -1
- package/dist/es/agent/tasks.mjs +3 -1
- package/dist/es/agent/tasks.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +4 -4
- package/dist/es/agent/utils.mjs.map +1 -1
- package/dist/es/ai-model/models/auto-glm/parser.mjs +1 -1
- package/dist/es/ai-model/models/auto-glm/parser.mjs.map +1 -1
- package/dist/es/ai-model/models/kimi.mjs +1 -1
- package/dist/es/ai-model/models/kimi.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/call-ai.mjs +10 -1
- package/dist/es/ai-model/service-caller/call-ai.mjs.map +1 -1
- package/dist/es/ai-model/service-caller/openai/call-openai.mjs +7 -2
- package/dist/es/ai-model/service-caller/openai/call-openai.mjs.map +1 -1
- package/dist/es/ai-model/shared/model-locate-result/errors.mjs +1 -1
- package/dist/es/ai-model/shared/model-locate-result/errors.mjs.map +1 -1
- package/dist/es/ai-model/shared/model-locate-result/parse.mjs +6 -2
- package/dist/es/ai-model/shared/model-locate-result/parse.mjs.map +1 -1
- package/dist/es/device/index.mjs +9 -8
- package/dist/es/device/index.mjs.map +1 -1
- package/dist/es/report-generator.mjs +76 -36
- package/dist/es/report-generator.mjs.map +1 -1
- package/dist/es/report-html-template.mjs +1 -1
- package/dist/es/test-run-report.mjs.map +1 -1
- package/dist/es/test-runner/engine/document-runtime.mjs +30 -24
- package/dist/es/test-runner/engine/document-runtime.mjs.map +1 -1
- package/dist/es/test-runner/engine/project-runtime.mjs +17 -11
- package/dist/es/test-runner/engine/project-runtime.mjs.map +1 -1
- package/dist/es/test-runner/engine/resource-operations.mjs +33 -8
- package/dist/es/test-runner/engine/resource-operations.mjs.map +1 -1
- package/dist/es/test-runner/engine/run-collected-case.mjs +1 -0
- package/dist/es/test-runner/engine/run-collected-case.mjs.map +1 -1
- package/dist/es/test-runner/engine/run-workflow-document.mjs +3 -0
- package/dist/es/test-runner/engine/run-workflow-document.mjs.map +1 -1
- package/dist/es/test-runner/engine/setup-timeout.mjs +37 -0
- package/dist/es/test-runner/engine/setup-timeout.mjs.map +1 -0
- package/dist/es/test-runner/reporting/merge-report.mjs +3 -1
- package/dist/es/test-runner/reporting/merge-report.mjs.map +1 -1
- package/dist/es/test-runner/reporting/metrics.mjs +39 -2
- package/dist/es/test-runner/reporting/metrics.mjs.map +1 -1
- package/dist/es/utils.mjs +1 -1
- package/dist/es/yaml/execution-session.mjs +2 -1
- package/dist/es/yaml/execution-session.mjs.map +1 -1
- package/dist/es/yaml/legacy-yaml-report.mjs +1 -1
- package/dist/es/yaml/legacy-yaml-report.mjs.map +1 -1
- package/dist/es/yaml/player.mjs +17 -3
- package/dist/es/yaml/player.mjs.map +1 -1
- package/dist/lib/agent/agent-options.js +73 -0
- package/dist/lib/agent/agent-options.js.map +1 -0
- package/dist/lib/agent/agent-report-manager.js +357 -0
- package/dist/lib/agent/agent-report-manager.js.map +1 -0
- package/dist/lib/agent/agent.js +110 -226
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/cache-config.js +23 -0
- package/dist/lib/agent/cache-config.js.map +1 -1
- package/dist/lib/agent/tasks.js +3 -1
- package/dist/lib/agent/tasks.js.map +1 -1
- package/dist/lib/agent/utils.js +2 -2
- package/dist/lib/agent/utils.js.map +1 -1
- package/dist/lib/ai-model/models/auto-glm/parser.js +1 -1
- package/dist/lib/ai-model/models/auto-glm/parser.js.map +1 -1
- package/dist/lib/ai-model/models/kimi.js +1 -1
- package/dist/lib/ai-model/models/kimi.js.map +1 -1
- package/dist/lib/ai-model/service-caller/call-ai.js +10 -1
- package/dist/lib/ai-model/service-caller/call-ai.js.map +1 -1
- package/dist/lib/ai-model/service-caller/openai/call-openai.js +7 -2
- package/dist/lib/ai-model/service-caller/openai/call-openai.js.map +1 -1
- package/dist/lib/ai-model/shared/model-locate-result/errors.js +1 -1
- package/dist/lib/ai-model/shared/model-locate-result/errors.js.map +1 -1
- package/dist/lib/ai-model/shared/model-locate-result/parse.js +6 -2
- package/dist/lib/ai-model/shared/model-locate-result/parse.js.map +1 -1
- package/dist/lib/device/index.js +9 -8
- package/dist/lib/device/index.js.map +1 -1
- package/dist/lib/report-generator.js +75 -35
- package/dist/lib/report-generator.js.map +1 -1
- package/dist/lib/report-html-template.js +1 -1
- package/dist/lib/test-run-report.js.map +1 -1
- package/dist/lib/test-runner/engine/document-runtime.js +30 -24
- package/dist/lib/test-runner/engine/document-runtime.js.map +1 -1
- package/dist/lib/test-runner/engine/project-runtime.js +17 -11
- package/dist/lib/test-runner/engine/project-runtime.js.map +1 -1
- package/dist/lib/test-runner/engine/resource-operations.js +36 -8
- package/dist/lib/test-runner/engine/resource-operations.js.map +1 -1
- package/dist/lib/test-runner/engine/run-collected-case.js +1 -0
- package/dist/lib/test-runner/engine/run-collected-case.js.map +1 -1
- package/dist/lib/test-runner/engine/run-workflow-document.js +3 -0
- package/dist/lib/test-runner/engine/run-workflow-document.js.map +1 -1
- package/dist/lib/test-runner/engine/setup-timeout.js +71 -0
- package/dist/lib/test-runner/engine/setup-timeout.js.map +1 -0
- package/dist/lib/test-runner/reporting/merge-report.js +3 -1
- package/dist/lib/test-runner/reporting/merge-report.js.map +1 -1
- package/dist/lib/test-runner/reporting/metrics.js +39 -2
- package/dist/lib/test-runner/reporting/metrics.js.map +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/lib/yaml/execution-session.js +4 -0
- package/dist/lib/yaml/execution-session.js.map +1 -1
- package/dist/lib/yaml/legacy-yaml-report.js +1 -1
- package/dist/lib/yaml/legacy-yaml-report.js.map +1 -1
- package/dist/lib/yaml/player.js +16 -2
- package/dist/lib/yaml/player.js.map +1 -1
- package/dist/types/agent/agent-options.d.ts +4 -0
- package/dist/types/agent/agent-options.d.ts.map +1 -0
- package/dist/types/agent/agent-report-manager.d.ts +92 -0
- package/dist/types/agent/agent-report-manager.d.ts.map +1 -0
- package/dist/types/agent/agent.d.ts +31 -24
- package/dist/types/agent/agent.d.ts.map +1 -1
- package/dist/types/agent/cache-config.d.ts +10 -0
- package/dist/types/agent/cache-config.d.ts.map +1 -1
- package/dist/types/agent/tasks.d.ts +1 -0
- package/dist/types/agent/tasks.d.ts.map +1 -1
- package/dist/types/ai-model/models/auto-glm/parser.d.ts.map +1 -1
- package/dist/types/ai-model/service-caller/openai/call-openai.d.ts.map +1 -1
- package/dist/types/ai-model/shared/model-locate-result/errors.d.ts +1 -1
- package/dist/types/ai-model/shared/model-locate-result/errors.d.ts.map +1 -1
- package/dist/types/ai-model/shared/model-locate-result/parse.d.ts.map +1 -1
- package/dist/types/device/index.d.ts +7 -7
- package/dist/types/device/index.d.ts.map +1 -1
- package/dist/types/report-generator.d.ts +5 -1
- package/dist/types/report-generator.d.ts.map +1 -1
- package/dist/types/test-run-report.d.ts +4 -1
- package/dist/types/test-run-report.d.ts.map +1 -1
- package/dist/types/test-runner/engine/document-runtime.d.ts.map +1 -1
- package/dist/types/test-runner/engine/project-runtime.d.ts.map +1 -1
- package/dist/types/test-runner/engine/resource-operations.d.ts +5 -0
- package/dist/types/test-runner/engine/resource-operations.d.ts.map +1 -1
- package/dist/types/test-runner/engine/run-collected-case.d.ts.map +1 -1
- package/dist/types/test-runner/engine/run-workflow-document.d.ts.map +1 -1
- package/dist/types/test-runner/engine/setup-timeout.d.ts +3 -0
- package/dist/types/test-runner/engine/setup-timeout.d.ts.map +1 -0
- package/dist/types/test-runner/engine/types.d.ts +7 -0
- package/dist/types/test-runner/engine/types.d.ts.map +1 -1
- package/dist/types/test-runner/parser/types.d.ts +5 -0
- package/dist/types/test-runner/parser/types.d.ts.map +1 -1
- package/dist/types/test-runner/project.d.ts +22 -0
- package/dist/types/test-runner/project.d.ts.map +1 -1
- package/dist/types/test-runner/reporting/merge-report.d.ts.map +1 -1
- package/dist/types/test-runner/reporting/metrics.d.ts.map +1 -1
- package/dist/types/yaml/execution-session.d.ts +2 -0
- package/dist/types/yaml/execution-session.d.ts.map +1 -1
- package/dist/types/yaml/player.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-run-report.mjs","sources":["../../src/test-run-report.ts"],"sourcesContent":["export const TEST_RUN_REPORT_SCRIPT_TYPE = 'midscene_test_run_dump';\n\nexport type TestRunReportRunStatus = 'success' | 'failed';\nexport type TestRunReportCaseStatus = TestRunReportRunStatus | 'not-run';\nexport type TestRunReportStepPhase =\n | 'beforeAll'\n | 'beforeEach'\n | 'steps'\n | 'afterEach'\n | 'afterAll';\n\n/**\n * A JSON-safe value prepared for display in a Midscene Test report.\n *\n * Paths use JSONPath-like notation rooted at `$`. The path arrays are kept\n * beside the value so the UI never presents redacted or truncated data as if\n * it were complete.\n */\nexport interface TestRunReportValue {\n value: unknown;\n truncatedPaths?: string[];\n redactedPaths?: string[];\n}\n\nexport interface TestRunReportError {\n format?: 'midscene-error';\n name: string;\n message: string;\n stack?: string;\n code?: string;\n details?: TestRunReportValue;\n cause?: TestRunReportError;\n errors?: TestRunReportError[];\n truncated?: boolean;\n}\n\nexport interface TestRunReportOperationReference {\n operationId: string;\n scopeId?: string;\n stepId?: string;\n node?: string;\n}\n\n/** Immutable observation; late completion remains in the operation journal. */\nexport interface TestRunReportCleanup {\n status: 'not-required' | 'completed' | 'pending' | 'failed';\n capturedAt: string;\n operationId?: string;\n blockingOperations?: TestRunReportOperationReference[];\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportRetryDecision {\n scope: 'case' | 'document';\n retryCount: number;\n attemptIndex: number;\n decision: 'retry' | 'stop';\n reason:\n | 'passed'\n | 'attempt-failed'\n | 'budget-exhausted'\n | 'cleanup-pending'\n | 'cleanup-failed'\n | 'operation-pending'\n | 'fatal-error'\n | 'interrupted'\n | 'bail'\n | 'infrastructure-error';\n}\n\nexport interface TestRunReportPhaseResult {\n status: 'success' | 'failed' | 'not-run' | 'not-required';\n plannedStepCount: number;\n executedStepCount: number;\n}\n\nexport interface TestRunReportHostError {\n phase:\n | 'setup'\n | 'execution'\n | 'cleanup'\n | 'report'\n | 'publication'\n | 'observer';\n error: TestRunReportError;\n}\n\nexport interface TestRunReportDiagnostic {\n level: 'warning' | 'error';\n code:\n | 'agent-detail-unresolved'\n | 'agent-detail-unavailable'\n | 'large-report'\n | 'run-infrastructure-error'\n | 'agent-source-unavailable'\n | 'source-without-executions';\n message: string;\n scopeId?: string;\n executionId?: string;\n sourcePaths?: string[];\n stepId?: string;\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportMetrics {\n modelCallCount: number;\n modelTimeMs: number;\n promptTokens: number;\n cachedInputTokens: number;\n completionTokens: number;\n totalTokens: number;\n collection?: {\n status: 'complete' | 'partial' | 'unknown';\n expectedExecutionCount: number;\n capturedExecutionCount: number;\n reasons: string[];\n fields?: Record<\n 'modelCallCount' | 'modelTimeMs' | 'totalTokens',\n 'complete' | 'partial' | 'unknown'\n >;\n };\n}\n\nexport interface TestRunReportSnapshotCollection {\n status: 'complete' | 'partial';\n /** Native dump cutoff before I/O; observation time for partial live sources. */\n capturedAt: string;\n reason?: string;\n}\n\nexport interface TestRunReportAgentDetail {\n reportId: string;\n executionId: string;\n}\n\n/** Operation facts are independent of Agent dump IDs and report snapshots. */\nexport interface TestRunReportOperationEvent {\n operationId: string;\n operationKind: 'node' | 'resource' | 'acquisition' | 'cleanup' | 'readiness';\n kind:\n | 'started'\n | 'cancel-requested'\n | 'settled'\n | 'runner-returned'\n | 'cleanup-deferred';\n at: string;\n stepId?: string;\n scopeId?: string;\n node?: string;\n status?: 'success' | 'failed' | 'not-run';\n /** True only when the actual operation has not settled at Runner return. */\n pending?: boolean;\n error?: TestRunReportError;\n blockingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportPlannedStep {\n id: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n input: TestRunReportValue;\n inputMode: 'template' | 'resolved';\n timeoutMs?: number;\n continueOnError: boolean;\n}\n\nexport interface TestRunReportCasePlan {\n caseId: string;\n name: string;\n caseIndex: number;\n selected: boolean;\n tags: string[];\n onFailure?: 'continue' | 'stop-document';\n beforeEach: TestRunReportPlannedStep[];\n steps: TestRunReportPlannedStep[];\n afterEach: TestRunReportPlannedStep[];\n}\n\n/** A safe snapshot collected before resource acquisition, independent of attempts. */\nexport interface TestRunReportDocumentPlan {\n documentId: string;\n sourcePath: string;\n sourceYaml?: TestRunReportValue;\n retry: { scope: 'case' | 'document'; count: number };\n beforeAll: TestRunReportPlannedStep[];\n cases: TestRunReportCasePlan[];\n afterAll: TestRunReportPlannedStep[];\n}\n\ninterface TestRunReportStepBase {\n id: string;\n planStepId?: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n continuedAfterError: boolean;\n /** Time spent waiting for a cancelled operation before this Step started. */\n waitForPreviousOperationMs?: number;\n input?: TestRunReportValue;\n output?: {\n summary?: string;\n data?: TestRunReportValue;\n };\n error?: TestRunReportError;\n agentDetails?: TestRunReportAgentDetail[];\n agentDetailDiagnostic?: string;\n /** Usage survives even when screenshots or execution detail storage fails. */\n metrics?: TestRunReportMetrics;\n evidenceCollection?: {\n status: 'complete' | 'partial' | 'unknown';\n /** The missing source also prevented independent usage recovery. */\n modelUsageUnavailable?: boolean;\n capturedAt?: string;\n errors?: TestRunReportError[];\n };\n operations?: { capturedAt: string; events: TestRunReportOperationEvent[] };\n}\n\nexport type TestRunReportStep = TestRunReportStepBase &\n (\n | {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n notRunReason?: never;\n blockedBy?: never;\n }\n | {\n status: 'not-run';\n notRunReason: string;\n blockedBy?: string;\n startedAt?: never;\n endedAt?: never;\n durationMs?: never;\n }\n );\n\nexport interface TestRunReportAttempt {\n attemptId: string;\n attemptIndex: number;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n beforeEach: TestRunReportStep[];\n steps: TestRunReportStep[];\n afterEach: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n}\n\nexport interface TestRunReportCase {\n plan?: TestRunReportCasePlan;\n caseId: string;\n name: string;\n caseIndex: number;\n status: TestRunReportCaseStatus;\n notRunReason?: string;\n attempts: TestRunReportAttempt[];\n}\n\nexport interface TestRunReportDocument {\n plan?: TestRunReportDocumentPlan;\n documentId: string;\n /** Logical file identity when separate invocations need distinct UI IDs. */\n logicalDocumentId?: string;\n documentRunId?: string;\n attemptIndex?: number;\n sourcePath: string;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt?: string;\n endedAt?: string;\n durationMs?: number;\n beforeAll: TestRunReportStep[];\n cases: TestRunReportCase[];\n afterAll: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n /** Whole-file invocation history; Cases aggregate their own attempts. */\n attempts?: Omit<TestRunReportDocument, 'cases' | 'attempts'>[];\n}\n\nexport interface TestRunReportCollectionError {\n sourcePath: string;\n error: TestRunReportError;\n}\n\nexport interface TestRunReportProjectLifecycle {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n setupError?: TestRunReportError;\n teardownErrors?: TestRunReportError[];\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportProject {\n plans?: readonly TestRunReportDocumentPlan[];\n projectId: string;\n name: string;\n /** Display identity, also supports legacy custom interface adapters. */\n platform?: string;\n status: TestRunReportRunStatus;\n retry: number;\n lifecycle?: TestRunReportProjectLifecycle;\n documents: TestRunReportDocument[];\n collectionErrors: TestRunReportCollectionError[];\n}\n\nexport interface TestRunReportSummary {\n total: number;\n passed: number;\n failed: number;\n notRun: number;\n filtered: number;\n collectionErrors: number;\n documentFailures: number;\n projectFailures: number;\n}\n\nexport interface TestRunReportDump {\n schemaVersion: 1;\n kind: 'test-runner';\n runId: string;\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n summary: TestRunReportSummary;\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n projects: TestRunReportProject[];\n diagnostics?: TestRunReportDiagnostic[];\n operationLogs?: { path: string; snapshotAt: string }[];\n}\n\nexport interface TestRunReportSource {\n /** Resource/report scope that owns this Midscene Agent source. */\n scopeId: string;\n /** Absolute path identifying the Midscene Agent report. */\n sourcePath: string;\n /** Current Agent dump JSON; when present, HTML is not parsed for execution data. */\n dumpPath?: string;\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface IndexedTestRunReportSource {\n reportId: string;\n scopeId: string;\n sourcePath: string;\n executionIds: string[];\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface TestRunReportSourceIndex {\n sources: IndexedTestRunReportSource[];\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n /** Usage from readable sources, before applying source-availability diagnostics. */\n capturedMetrics?: TestRunReportMetrics;\n diagnostics?: TestRunReportDiagnostic[];\n}\n\nexport interface AssembleTestRunReportOptions {\n outputDir: string;\n /** File or directory name without an extension. */\n reportFileName: string;\n sources: readonly TestRunReportSource[];\n buildRunnerDump(index: TestRunReportSourceIndex): TestRunReportDump;\n overwrite?: boolean;\n}\n"],"names":["TEST_RUN_REPORT_SCRIPT_TYPE"],"mappings":"AAAO,MAAMA,8BAA8B"}
|
|
1
|
+
{"version":3,"file":"test-run-report.mjs","sources":["../../src/test-run-report.ts"],"sourcesContent":["import type { ReportModelUsageSummary } from './report-stats';\n\nexport const TEST_RUN_REPORT_SCRIPT_TYPE = 'midscene_test_run_dump';\n\nexport type TestRunReportRunStatus = 'success' | 'failed';\nexport type TestRunReportCaseStatus = TestRunReportRunStatus | 'not-run';\nexport type TestRunReportStepPhase =\n | 'beforeAll'\n | 'beforeEach'\n | 'steps'\n | 'afterEach'\n | 'afterAll';\n\n/**\n * A JSON-safe value prepared for display in a Midscene Test report.\n *\n * Paths use JSONPath-like notation rooted at `$`. The path arrays are kept\n * beside the value so the UI never presents redacted or truncated data as if\n * it were complete.\n */\nexport interface TestRunReportValue {\n value: unknown;\n truncatedPaths?: string[];\n redactedPaths?: string[];\n}\n\nexport interface TestRunReportError {\n format?: 'midscene-error';\n name: string;\n message: string;\n stack?: string;\n code?: string;\n details?: TestRunReportValue;\n cause?: TestRunReportError;\n errors?: TestRunReportError[];\n truncated?: boolean;\n}\n\nexport interface TestRunReportOperationReference {\n operationId: string;\n scopeId?: string;\n stepId?: string;\n node?: string;\n}\n\n/** Immutable observation; late completion remains in the operation journal. */\nexport interface TestRunReportCleanup {\n status: 'not-required' | 'completed' | 'pending' | 'failed';\n capturedAt: string;\n operationId?: string;\n blockingOperations?: TestRunReportOperationReference[];\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportRetryDecision {\n scope: 'case' | 'document';\n retryCount: number;\n attemptIndex: number;\n decision: 'retry' | 'stop';\n reason:\n | 'passed'\n | 'attempt-failed'\n | 'budget-exhausted'\n | 'cleanup-pending'\n | 'cleanup-failed'\n | 'operation-pending'\n | 'fatal-error'\n | 'interrupted'\n | 'bail'\n | 'infrastructure-error';\n}\n\nexport interface TestRunReportPhaseResult {\n status: 'success' | 'failed' | 'not-run' | 'not-required';\n plannedStepCount: number;\n executedStepCount: number;\n}\n\nexport interface TestRunReportHostError {\n phase:\n | 'setup'\n | 'execution'\n | 'cleanup'\n | 'report'\n | 'publication'\n | 'observer';\n error: TestRunReportError;\n}\n\nexport interface TestRunReportDiagnostic {\n level: 'warning' | 'error';\n code:\n | 'agent-detail-unresolved'\n | 'agent-detail-unavailable'\n | 'large-report'\n | 'run-infrastructure-error'\n | 'agent-source-unavailable'\n | 'source-without-executions';\n message: string;\n scopeId?: string;\n executionId?: string;\n sourcePaths?: string[];\n stepId?: string;\n error?: TestRunReportError;\n}\n\nexport interface TestRunReportMetrics {\n /** Actual models observed in usage records, independent of configured defaults. */\n models?: ReportModelUsageSummary[];\n modelCallCount: number;\n modelTimeMs: number;\n promptTokens: number;\n cachedInputTokens: number;\n completionTokens: number;\n totalTokens: number;\n collection?: {\n status: 'complete' | 'partial' | 'unknown';\n expectedExecutionCount: number;\n capturedExecutionCount: number;\n reasons: string[];\n fields?: Record<\n 'modelCallCount' | 'modelTimeMs' | 'totalTokens',\n 'complete' | 'partial' | 'unknown'\n > &\n Partial<\n Record<\n 'promptTokens' | 'completionTokens',\n 'complete' | 'partial' | 'unknown'\n >\n >;\n };\n}\n\nexport interface TestRunReportSnapshotCollection {\n status: 'complete' | 'partial';\n /** Native dump cutoff before I/O; observation time for partial live sources. */\n capturedAt: string;\n reason?: string;\n}\n\nexport interface TestRunReportAgentDetail {\n reportId: string;\n executionId: string;\n}\n\n/** Operation facts are independent of Agent dump IDs and report snapshots. */\nexport interface TestRunReportOperationEvent {\n operationId: string;\n operationKind: 'node' | 'resource' | 'acquisition' | 'cleanup' | 'readiness';\n kind:\n | 'started'\n | 'cancel-requested'\n | 'settled'\n | 'runner-returned'\n | 'cleanup-deferred';\n at: string;\n stepId?: string;\n scopeId?: string;\n node?: string;\n status?: 'success' | 'failed' | 'not-run';\n /** True only when the actual operation has not settled at Runner return. */\n pending?: boolean;\n error?: TestRunReportError;\n blockingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportPlannedStep {\n id: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n input: TestRunReportValue;\n inputMode: 'template' | 'resolved';\n timeoutMs?: number;\n continueOnError: boolean;\n}\n\nexport interface TestRunReportCasePlan {\n caseId: string;\n name: string;\n caseIndex: number;\n selected: boolean;\n tags: string[];\n onFailure?: 'continue' | 'stop-document';\n beforeEach: TestRunReportPlannedStep[];\n steps: TestRunReportPlannedStep[];\n afterEach: TestRunReportPlannedStep[];\n}\n\n/** A safe snapshot collected before resource acquisition, independent of attempts. */\nexport interface TestRunReportDocumentPlan {\n documentId: string;\n sourcePath: string;\n sourceYaml?: TestRunReportValue;\n retry: { scope: 'case' | 'document'; count: number };\n beforeAll: TestRunReportPlannedStep[];\n cases: TestRunReportCasePlan[];\n afterAll: TestRunReportPlannedStep[];\n}\n\ninterface TestRunReportStepBase {\n id: string;\n planStepId?: string;\n phase: TestRunReportStepPhase;\n stepIndex: number;\n node: string;\n title?: string;\n continuedAfterError: boolean;\n /** Time spent waiting for a cancelled operation before this Step started. */\n waitForPreviousOperationMs?: number;\n input?: TestRunReportValue;\n output?: {\n summary?: string;\n data?: TestRunReportValue;\n };\n error?: TestRunReportError;\n agentDetails?: TestRunReportAgentDetail[];\n agentDetailDiagnostic?: string;\n /** Usage survives even when screenshots or execution detail storage fails. */\n metrics?: TestRunReportMetrics;\n evidenceCollection?: {\n status: 'complete' | 'partial' | 'unknown';\n /** The missing source also prevented independent usage recovery. */\n modelUsageUnavailable?: boolean;\n capturedAt?: string;\n errors?: TestRunReportError[];\n };\n operations?: { capturedAt: string; events: TestRunReportOperationEvent[] };\n}\n\nexport type TestRunReportStep = TestRunReportStepBase &\n (\n | {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n notRunReason?: never;\n blockedBy?: never;\n }\n | {\n status: 'not-run';\n notRunReason: string;\n blockedBy?: string;\n startedAt?: never;\n endedAt?: never;\n durationMs?: never;\n }\n );\n\nexport interface TestRunReportAttempt {\n attemptId: string;\n attemptIndex: number;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n beforeEach: TestRunReportStep[];\n steps: TestRunReportStep[];\n afterEach: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n}\n\nexport interface TestRunReportCase {\n plan?: TestRunReportCasePlan;\n caseId: string;\n name: string;\n caseIndex: number;\n status: TestRunReportCaseStatus;\n notRunReason?: string;\n attempts: TestRunReportAttempt[];\n}\n\nexport interface TestRunReportDocument {\n plan?: TestRunReportDocumentPlan;\n documentId: string;\n /** Logical file identity when separate invocations need distinct UI IDs. */\n logicalDocumentId?: string;\n documentRunId?: string;\n attemptIndex?: number;\n sourcePath: string;\n status: TestRunReportRunStatus;\n executionStatus?: TestRunReportCaseStatus;\n phases?: Partial<Record<TestRunReportStepPhase, TestRunReportPhaseResult>>;\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n retryDecision?: TestRunReportRetryDecision;\n startedAt?: string;\n endedAt?: string;\n durationMs?: number;\n beforeAll: TestRunReportStep[];\n cases: TestRunReportCase[];\n afterAll: TestRunReportStep[];\n teardownErrors?: TestRunReportError[];\n hostErrors?: TestRunReportHostError[];\n /** Report groups available for scope-level fallback browsing. */\n scopeReportIds?: string[];\n /** Whole-file invocation history; Cases aggregate their own attempts. */\n attempts?: Omit<TestRunReportDocument, 'cases' | 'attempts'>[];\n}\n\nexport interface TestRunReportCollectionError {\n sourcePath: string;\n error: TestRunReportError;\n}\n\nexport interface TestRunReportProjectLifecycle {\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n setupError?: TestRunReportError;\n teardownErrors?: TestRunReportError[];\n cleanup?: TestRunReportCleanup;\n pendingOperations?: TestRunReportOperationReference[];\n}\n\nexport interface TestRunReportProject {\n plans?: readonly TestRunReportDocumentPlan[];\n projectId: string;\n name: string;\n /** Display identity, also supports legacy custom interface adapters. */\n platform?: string;\n status: TestRunReportRunStatus;\n retry: number;\n lifecycle?: TestRunReportProjectLifecycle;\n documents: TestRunReportDocument[];\n collectionErrors: TestRunReportCollectionError[];\n}\n\nexport interface TestRunReportSummary {\n total: number;\n passed: number;\n failed: number;\n notRun: number;\n filtered: number;\n collectionErrors: number;\n documentFailures: number;\n projectFailures: number;\n}\n\nexport interface TestRunReportDump {\n schemaVersion: 1;\n kind: 'test-runner';\n runId: string;\n status: TestRunReportRunStatus;\n startedAt: string;\n endedAt: string;\n durationMs: number;\n summary: TestRunReportSummary;\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n projects: TestRunReportProject[];\n diagnostics?: TestRunReportDiagnostic[];\n operationLogs?: { path: string; snapshotAt: string }[];\n}\n\nexport interface TestRunReportSource {\n /** Resource/report scope that owns this Midscene Agent source. */\n scopeId: string;\n /** Absolute path identifying the Midscene Agent report. */\n sourcePath: string;\n /** Current Agent dump JSON; when present, HTML is not parsed for execution data. */\n dumpPath?: string;\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface IndexedTestRunReportSource {\n reportId: string;\n scopeId: string;\n sourcePath: string;\n executionIds: string[];\n collection?: TestRunReportSnapshotCollection;\n}\n\nexport interface TestRunReportSourceIndex {\n sources: IndexedTestRunReportSource[];\n metrics: TestRunReportMetrics;\n executionMetrics?: Record<string, TestRunReportMetrics>;\n /** Usage from readable sources, before applying source-availability diagnostics. */\n capturedMetrics?: TestRunReportMetrics;\n diagnostics?: TestRunReportDiagnostic[];\n}\n\nexport interface AssembleTestRunReportOptions {\n outputDir: string;\n /** File or directory name without an extension. */\n reportFileName: string;\n sources: readonly TestRunReportSource[];\n buildRunnerDump(index: TestRunReportSourceIndex): TestRunReportDump;\n overwrite?: boolean;\n}\n"],"names":["TEST_RUN_REPORT_SCRIPT_TYPE"],"mappings":"AAEO,MAAMA,8BAA8B"}
|
|
@@ -7,6 +7,7 @@ import { WorkflowExecutionFailure, asExecutionError } from "./execution-failure.
|
|
|
7
7
|
import { ResourceCleanupDeferredError, cleanupScopeResources, createResourceScope, getPendingScopeOperations, getResourceCleanupSnapshot, recoverCancelledScopeResources, setResourceOperationIdentity } from "./resource-operations.mjs";
|
|
8
8
|
import { phaseResult } from "./result-metadata.mjs";
|
|
9
9
|
import { reportPathsFromTeardown } from "./scope-teardown.mjs";
|
|
10
|
+
import { runSetupWithTimeout } from "./setup-timeout.mjs";
|
|
10
11
|
function createDocumentRuntime(document, options) {
|
|
11
12
|
const documentRunId = options.createDocumentRunId?.() ?? uuid();
|
|
12
13
|
const plan = options.plan ?? buildTestRunReportPlan(document, {
|
|
@@ -172,36 +173,40 @@ function createDocumentRuntime(document, options) {
|
|
|
172
173
|
if (!signal.aborted && options.documentSetup) {
|
|
173
174
|
acceptingSetupTeardowns = true;
|
|
174
175
|
try {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
176
|
+
await runSetupWithTimeout(async ()=>{
|
|
177
|
+
try {
|
|
178
|
+
context = await options.documentSetup.setup({
|
|
179
|
+
project,
|
|
180
|
+
projectContext: options.projectContext,
|
|
181
|
+
document: {
|
|
182
|
+
documentId: document.documentId,
|
|
183
|
+
documentRunId,
|
|
184
|
+
sourcePath: document.sourcePath,
|
|
185
|
+
attemptIndex: options.documentAttemptIndex ?? 0
|
|
186
|
+
},
|
|
187
|
+
signal,
|
|
188
|
+
onTeardown (teardown) {
|
|
189
|
+
if (!acceptingSetupTeardowns || 'function' != typeof teardown) throw new WorkflowLifecycleError('Document setup onTeardown() requires a function registered during setup.', {
|
|
190
|
+
documentId: document.documentId,
|
|
191
|
+
documentRunId
|
|
192
|
+
});
|
|
193
|
+
nodeTeardownStack.push({
|
|
194
|
+
registrationIndex: nodeTeardownStack.length,
|
|
195
|
+
node: options.documentSetup.name,
|
|
196
|
+
teardown,
|
|
197
|
+
host: true
|
|
198
|
+
});
|
|
199
|
+
}
|
|
195
200
|
});
|
|
201
|
+
} finally{
|
|
202
|
+
acceptingSetupTeardowns = false;
|
|
196
203
|
}
|
|
197
|
-
});
|
|
204
|
+
}, signal, resourceScope.abort, options.setupTimeoutMs);
|
|
198
205
|
} catch (error) {
|
|
199
206
|
hostErrors.push({
|
|
200
207
|
phase: 'setup',
|
|
201
208
|
error: asExecutionError(error)
|
|
202
209
|
});
|
|
203
|
-
} finally{
|
|
204
|
-
acceptingSetupTeardowns = false;
|
|
205
210
|
}
|
|
206
211
|
}
|
|
207
212
|
try {
|
|
@@ -258,7 +263,8 @@ function createDocumentRuntime(document, options) {
|
|
|
258
263
|
}
|
|
259
264
|
};
|
|
260
265
|
try {
|
|
261
|
-
if (document.lifecycle.afterAll.length || nodeTeardownStack.length) await cleanupScopeResources(signal, cleanup, {
|
|
266
|
+
if (document.lifecycle.afterAll.length || nodeTeardownStack.length || void 0 !== options.setupTimeoutMs && getPendingScopeOperations(signal).length) await cleanupScopeResources(signal, cleanup, {
|
|
267
|
+
totalTimeoutMs: options.teardownTimeoutMs?.(),
|
|
262
268
|
beforeCleanup: async (cleanupSignal)=>{
|
|
263
269
|
try {
|
|
264
270
|
if (!hostErrors.some((error)=>'setup' === error.phase)) await runPhase('afterAll', afterAll, cleanupSignal);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-runner/engine/document-runtime.mjs","sources":["../../../../src/test-runner/engine/document-runtime.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { uuid as randomUUID } from '@midscene/shared/utils';\nimport {\n NodeScopeTeardownError,\n type WorkflowError,\n WorkflowLifecycleError,\n} from '../errors';\nimport type { CollectedWorkflowDocument } from '../parser/types';\nimport { buildTestRunReportPlan } from '../reporting/test-plan';\nimport { executeStep } from './execute-step';\nimport {\n WorkflowExecutionFailure,\n asExecutionError,\n} from './execution-failure';\nimport {\n ResourceCleanupDeferredError,\n cleanupScopeResources,\n createResourceScope,\n getPendingScopeOperations,\n getResourceCleanupSnapshot,\n recoverCancelledScopeResources,\n setResourceOperationIdentity,\n} from './resource-operations';\nimport { phaseResult } from './result-metadata';\nimport { reportPathsFromTeardown } from './scope-teardown';\nimport type {\n CreateDocumentRuntimeOptions,\n DocumentNodePhase,\n NodeDocumentContext,\n NodeScopeTeardown,\n StepRunResult,\n WorkflowDocumentRunResult,\n WorkflowDocumentRuntime,\n WorkflowHostError,\n} from './types';\n\nexport function createDocumentRuntime<TContext = undefined>(\n document: CollectedWorkflowDocument,\n options: CreateDocumentRuntimeOptions<TContext>,\n): WorkflowDocumentRuntime<TContext> {\n const documentRunId = options.createDocumentRunId?.() ?? randomUUID();\n const plan =\n options.plan ??\n buildTestRunReportPlan(document, {\n defaultTimeoutMs: options.defaultTimeoutMs,\n retry: { scope: 'case', count: options.project?.retry ?? 0 },\n });\n const startedAt = new Date();\n const beforeAll: StepRunResult[] = [];\n const afterAll: StepRunResult[] = [];\n const nodes = {\n beforeAll: document.lifecycle.beforeAll.map((step) =>\n options.resolveNode(step.node),\n ),\n afterAll: document.lifecycle.afterAll.map((step) =>\n options.resolveNode(step.node),\n ),\n };\n const project =\n options.project ??\n Object.freeze({\n projectId: document.projectId,\n name: document.projectId,\n retry: 0,\n });\n let context = options.projectContext as TContext;\n const resourceScope = createResourceScope(options.signal);\n const signal = resourceScope.signal;\n setResourceOperationIdentity(signal, { scopeId: documentRunId });\n const nodeTeardownStack: Array<{\n registrationIndex: number;\n node: string;\n teardown: NodeScopeTeardown;\n host?: boolean;\n }> = [];\n let acceptingNodeTeardowns = true;\n let started = false;\n let finishedResult: WorkflowDocumentRunResult | undefined;\n let finishPromise: Promise<WorkflowDocumentRunResult> | undefined;\n const reportPaths = new Set<string>();\n const reportSources: import('./types').WorkflowReportSource[] = [];\n const capturedSources = new Map<\n string,\n import('./types').WorkflowReportSource\n >();\n const executionErrors: unknown[] = [];\n const hostErrors: WorkflowHostError[] = [];\n let acceptingSetupTeardowns = false;\n\n const createResult = (\n teardownErrors: WorkflowError[] = [],\n ): WorkflowDocumentRunResult => {\n const endedAt = new Date();\n const cleanup = getResourceCleanupSnapshot(signal);\n const pendingOperations = getPendingScopeOperations(signal);\n const phaseResults = {\n beforeAll: phaseResult(document.lifecycle.beforeAll.length, beforeAll),\n afterAll: phaseResult(document.lifecycle.afterAll.length, afterAll),\n };\n return {\n plan,\n documentId: document.documentId,\n documentRunId,\n ...(options.documentAttemptIndex === undefined\n ? {}\n : { attemptIndex: options.documentAttemptIndex }),\n projectId: document.projectId,\n projectName: project.name,\n sourcePath: document.sourcePath,\n status:\n executionErrors.length > 0 ||\n hostErrors.length > 0 ||\n beforeAll.some((step) => step.status !== 'success') ||\n afterAll.some((step) => step.status !== 'success') ||\n cleanup.status === 'pending' ||\n cleanup.status === 'failed' ||\n pendingOperations.length > 0 ||\n teardownErrors.length > 0\n ? 'failed'\n : 'success',\n executionStatus: Object.values(phaseResults).some(\n (phase) => phase.status === 'failed',\n )\n ? 'failed'\n : Object.values(phaseResults).some(\n (phase) => phase.status === 'not-run',\n ) ||\n hostErrors.some(({ phase }) => phase === 'setup') ||\n (signal.aborted && !beforeAll.length && !afterAll.length)\n ? 'not-run'\n : 'success',\n phases: phaseResults,\n cleanup,\n ...(pendingOperations.length ? { pendingOperations } : {}),\n startedAt: startedAt.toISOString(),\n endedAt: endedAt.toISOString(),\n durationMs: Math.max(0, endedAt.getTime() - startedAt.getTime()),\n beforeAll: [...beforeAll],\n afterAll: [...afterAll],\n ...(executionErrors.length\n ? {\n executionErrors: executionErrors\n .filter(\n (error) => !hostErrors.some((host) => host.error === error),\n )\n .map(asExecutionError),\n }\n : {}),\n ...(hostErrors.length ? { hostErrors: [...hostErrors] } : {}),\n ...(teardownErrors.length > 0\n ? { teardownErrors: [...teardownErrors] }\n : {}),\n ...(reportPaths.size > 0 ? { reportPaths: [...reportPaths] } : {}),\n ...(capturedSources.size || reportSources.length\n ? { reportSources: [...capturedSources.values(), ...reportSources] }\n : {}),\n };\n };\n\n const runPhase = async (\n phase: DocumentNodePhase,\n results: StepRunResult[],\n executionSignal = signal,\n ): Promise<void> => {\n for (const [stepIndex, step] of document.lifecycle[phase].entries()) {\n const waitForPreviousOperationMs =\n stepIndex > 0\n ? await recoverCancelledScopeResources(executionSignal)\n : 0;\n const documentContext: NodeDocumentContext = {\n documentId: document.documentId,\n documentRunId,\n projectId: document.projectId,\n projectName: project.name,\n sourcePath: document.sourcePath,\n phase,\n stepIndex,\n };\n const stepInfo = {\n scope: 'document' as const,\n node: step.node,\n stepCount: document.lifecycle[phase].length,\n document: documentContext,\n };\n await options.onStepStart?.(stepInfo);\n const result = await executeStep(\n step,\n nodes[phase][stepIndex],\n { scope: 'document', document: documentContext },\n context,\n {\n signal: executionSignal,\n defaultTimeoutMs: options.defaultTimeoutMs,\n onTeardown(node, teardown) {\n if (!acceptingNodeTeardowns) {\n throw new WorkflowLifecycleError(\n 'Node teardown can only be registered while a workflow document is running.',\n { documentId: document.documentId, documentRunId, node },\n );\n }\n if (typeof teardown !== 'function') {\n throw new WorkflowLifecycleError(\n 'Node onTeardown() requires a teardown function.',\n { documentId: document.documentId, documentRunId, node },\n );\n }\n nodeTeardownStack.push({\n registrationIndex: nodeTeardownStack.length,\n node,\n teardown,\n });\n },\n },\n );\n if (waitForPreviousOperationMs > 0)\n result.waitForPreviousOperationMs = waitForPreviousOperationMs;\n results.push(result);\n for (const source of result.report?.sources ?? [])\n capturedSources.set(\n `${source.sourcePath}\\u0000${source.dumpPath ?? ''}`,\n source,\n );\n await options.onStepResult?.(stepInfo, result);\n if (result.status !== 'success' && !result.continuedAfterError) break;\n }\n };\n\n return {\n signal,\n get context() {\n return context;\n },\n get canRunCases() {\n return (\n started &&\n executionErrors.length === 0 &&\n hostErrors.length === 0 &&\n !signal.aborted &&\n !beforeAll.some((step) => step.status !== 'success')\n );\n },\n async start() {\n if (started) {\n throw new WorkflowLifecycleError(\n 'Workflow document runtime has already started.',\n { documentId: document.documentId, documentRunId },\n );\n }\n started = true;\n if (!signal.aborted && options.documentSetup) {\n acceptingSetupTeardowns = true;\n try {\n context = await options.documentSetup.setup({\n project,\n projectContext: options.projectContext,\n document: {\n documentId: document.documentId,\n documentRunId,\n sourcePath: document.sourcePath,\n attemptIndex: options.documentAttemptIndex ?? 0,\n },\n signal,\n onTeardown(teardown) {\n if (!acceptingSetupTeardowns || typeof teardown !== 'function')\n throw new WorkflowLifecycleError(\n 'Document setup onTeardown() requires a function registered during setup.',\n { documentId: document.documentId, documentRunId },\n );\n nodeTeardownStack.push({\n registrationIndex: nodeTeardownStack.length,\n node: options.documentSetup!.name,\n teardown,\n host: true,\n });\n },\n });\n } catch (error) {\n hostErrors.push({ phase: 'setup', error: asExecutionError(error) });\n } finally {\n acceptingSetupTeardowns = false;\n }\n }\n try {\n if (!signal.aborted && !hostErrors.length)\n await runPhase('beforeAll', beforeAll);\n } catch (error) {\n executionErrors.push(error);\n throw new WorkflowExecutionFailure(createResult(), [\n ...executionErrors,\n ]);\n }\n return createResult();\n },\n async finish() {\n if (finishPromise) return finishPromise;\n if (!started) {\n throw new WorkflowLifecycleError(\n 'Workflow document runtime must start before it can finish.',\n { documentId: document.documentId, documentRunId },\n );\n }\n finishPromise = (async () => {\n const teardownErrors: WorkflowError[] = [];\n const cleanupExecutionErrors: unknown[] = [];\n let deferredCleanup = false;\n const cleanup = async () => {\n const priorTeardownErrors = teardownErrors.length;\n acceptingNodeTeardowns = false;\n for (const {\n registrationIndex,\n node,\n teardown,\n host,\n } of nodeTeardownStack.reverse()) {\n try {\n const released = await teardown();\n reportSources.push(...(released?.reportSources ?? []));\n for (const path of reportPathsFromTeardown(released)) {\n reportPaths.add(path);\n }\n } catch (error) {\n if (host) {\n const failure = asExecutionError(error);\n hostErrors.push({\n phase: 'cleanup',\n error: failure,\n });\n executionErrors.push(failure);\n cleanupExecutionErrors.push(error);\n continue;\n }\n teardownErrors.push(\n new NodeScopeTeardownError(error, {\n scope: 'document',\n scopeId: documentRunId,\n node,\n registrationIndex,\n }),\n );\n }\n }\n {\n const errors = [\n ...cleanupExecutionErrors,\n ...teardownErrors.slice(priorTeardownErrors),\n ];\n if (errors.length)\n throw new AggregateError(errors, 'Document cleanup failed');\n }\n };\n try {\n if (document.lifecycle.afterAll.length || nodeTeardownStack.length)\n await cleanupScopeResources(signal, cleanup, {\n beforeCleanup: async (cleanupSignal) => {\n try {\n if (!hostErrors.some((error) => error.phase === 'setup'))\n await runPhase('afterAll', afterAll, cleanupSignal);\n } catch (error) {\n executionErrors.push(error);\n cleanupExecutionErrors.push(error);\n }\n },\n onDeferredError: (error) =>\n getDebug('runner:cleanup', { console: true })(\n `Deferred document cleanup failed: ${String(error)}`,\n ),\n });\n else acceptingNodeTeardowns = false;\n } catch (error) {\n deferredCleanup = error instanceof ResourceCleanupDeferredError;\n if (!deferredCleanup) acceptingNodeTeardowns = false;\n if (\n !deferredCleanup &&\n !teardownErrors.length &&\n !cleanupExecutionErrors.length\n )\n teardownErrors.push(\n new NodeScopeTeardownError(error, {\n scope: 'document',\n scopeId: documentRunId,\n node: 'resource-drain',\n registrationIndex: -1,\n }),\n );\n }\n finishedResult = createResult(teardownErrors);\n try {\n await options.onResult?.(finishedResult);\n } catch (error) {\n executionErrors.push(error);\n finishedResult = createResult(teardownErrors);\n }\n resourceScope.dispose();\n if (executionErrors.length)\n throw new WorkflowExecutionFailure(finishedResult, [\n ...executionErrors,\n ]);\n return finishedResult;\n })();\n return finishPromise;\n },\n };\n}\n"],"names":["createDocumentRuntime","document","options","documentRunId","randomUUID","plan","buildTestRunReportPlan","startedAt","Date","beforeAll","afterAll","nodes","step","project","Object","context","resourceScope","createResourceScope","signal","setResourceOperationIdentity","nodeTeardownStack","acceptingNodeTeardowns","started","finishedResult","finishPromise","reportPaths","Set","reportSources","capturedSources","Map","executionErrors","hostErrors","acceptingSetupTeardowns","createResult","teardownErrors","endedAt","cleanup","getResourceCleanupSnapshot","pendingOperations","getPendingScopeOperations","phaseResults","phaseResult","undefined","phase","Math","error","host","asExecutionError","runPhase","results","executionSignal","stepIndex","waitForPreviousOperationMs","recoverCancelledScopeResources","documentContext","stepInfo","result","executeStep","node","teardown","WorkflowLifecycleError","source","WorkflowExecutionFailure","cleanupExecutionErrors","deferredCleanup","priorTeardownErrors","registrationIndex","released","path","reportPathsFromTeardown","failure","NodeScopeTeardownError","errors","AggregateError","cleanupScopeResources","cleanupSignal","getDebug","String","ResourceCleanupDeferredError"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"test-runner/engine/document-runtime.mjs","sources":["../../../../src/test-runner/engine/document-runtime.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport { uuid as randomUUID } from '@midscene/shared/utils';\nimport {\n NodeScopeTeardownError,\n type WorkflowError,\n WorkflowLifecycleError,\n} from '../errors';\nimport type { CollectedWorkflowDocument } from '../parser/types';\nimport { buildTestRunReportPlan } from '../reporting/test-plan';\nimport { executeStep } from './execute-step';\nimport {\n WorkflowExecutionFailure,\n asExecutionError,\n} from './execution-failure';\nimport {\n ResourceCleanupDeferredError,\n cleanupScopeResources,\n createResourceScope,\n getPendingScopeOperations,\n getResourceCleanupSnapshot,\n recoverCancelledScopeResources,\n setResourceOperationIdentity,\n} from './resource-operations';\nimport { phaseResult } from './result-metadata';\nimport { reportPathsFromTeardown } from './scope-teardown';\nimport { runSetupWithTimeout } from './setup-timeout';\nimport type {\n CreateDocumentRuntimeOptions,\n DocumentNodePhase,\n NodeDocumentContext,\n NodeScopeTeardown,\n StepRunResult,\n WorkflowDocumentRunResult,\n WorkflowDocumentRuntime,\n WorkflowHostError,\n} from './types';\n\nexport function createDocumentRuntime<TContext = undefined>(\n document: CollectedWorkflowDocument,\n options: CreateDocumentRuntimeOptions<TContext>,\n): WorkflowDocumentRuntime<TContext> {\n const documentRunId = options.createDocumentRunId?.() ?? randomUUID();\n const plan =\n options.plan ??\n buildTestRunReportPlan(document, {\n defaultTimeoutMs: options.defaultTimeoutMs,\n retry: { scope: 'case', count: options.project?.retry ?? 0 },\n });\n const startedAt = new Date();\n const beforeAll: StepRunResult[] = [];\n const afterAll: StepRunResult[] = [];\n const nodes = {\n beforeAll: document.lifecycle.beforeAll.map((step) =>\n options.resolveNode(step.node),\n ),\n afterAll: document.lifecycle.afterAll.map((step) =>\n options.resolveNode(step.node),\n ),\n };\n const project =\n options.project ??\n Object.freeze({\n projectId: document.projectId,\n name: document.projectId,\n retry: 0,\n });\n let context = options.projectContext as TContext;\n const resourceScope = createResourceScope(options.signal);\n const signal = resourceScope.signal;\n setResourceOperationIdentity(signal, { scopeId: documentRunId });\n const nodeTeardownStack: Array<{\n registrationIndex: number;\n node: string;\n teardown: NodeScopeTeardown;\n host?: boolean;\n }> = [];\n let acceptingNodeTeardowns = true;\n let started = false;\n let finishedResult: WorkflowDocumentRunResult | undefined;\n let finishPromise: Promise<WorkflowDocumentRunResult> | undefined;\n const reportPaths = new Set<string>();\n const reportSources: import('./types').WorkflowReportSource[] = [];\n const capturedSources = new Map<\n string,\n import('./types').WorkflowReportSource\n >();\n const executionErrors: unknown[] = [];\n const hostErrors: WorkflowHostError[] = [];\n let acceptingSetupTeardowns = false;\n\n const createResult = (\n teardownErrors: WorkflowError[] = [],\n ): WorkflowDocumentRunResult => {\n const endedAt = new Date();\n const cleanup = getResourceCleanupSnapshot(signal);\n const pendingOperations = getPendingScopeOperations(signal);\n const phaseResults = {\n beforeAll: phaseResult(document.lifecycle.beforeAll.length, beforeAll),\n afterAll: phaseResult(document.lifecycle.afterAll.length, afterAll),\n };\n return {\n plan,\n documentId: document.documentId,\n documentRunId,\n ...(options.documentAttemptIndex === undefined\n ? {}\n : { attemptIndex: options.documentAttemptIndex }),\n projectId: document.projectId,\n projectName: project.name,\n sourcePath: document.sourcePath,\n status:\n executionErrors.length > 0 ||\n hostErrors.length > 0 ||\n beforeAll.some((step) => step.status !== 'success') ||\n afterAll.some((step) => step.status !== 'success') ||\n cleanup.status === 'pending' ||\n cleanup.status === 'failed' ||\n pendingOperations.length > 0 ||\n teardownErrors.length > 0\n ? 'failed'\n : 'success',\n executionStatus: Object.values(phaseResults).some(\n (phase) => phase.status === 'failed',\n )\n ? 'failed'\n : Object.values(phaseResults).some(\n (phase) => phase.status === 'not-run',\n ) ||\n hostErrors.some(({ phase }) => phase === 'setup') ||\n (signal.aborted && !beforeAll.length && !afterAll.length)\n ? 'not-run'\n : 'success',\n phases: phaseResults,\n cleanup,\n ...(pendingOperations.length ? { pendingOperations } : {}),\n startedAt: startedAt.toISOString(),\n endedAt: endedAt.toISOString(),\n durationMs: Math.max(0, endedAt.getTime() - startedAt.getTime()),\n beforeAll: [...beforeAll],\n afterAll: [...afterAll],\n ...(executionErrors.length\n ? {\n executionErrors: executionErrors\n .filter(\n (error) => !hostErrors.some((host) => host.error === error),\n )\n .map(asExecutionError),\n }\n : {}),\n ...(hostErrors.length ? { hostErrors: [...hostErrors] } : {}),\n ...(teardownErrors.length > 0\n ? { teardownErrors: [...teardownErrors] }\n : {}),\n ...(reportPaths.size > 0 ? { reportPaths: [...reportPaths] } : {}),\n ...(capturedSources.size || reportSources.length\n ? { reportSources: [...capturedSources.values(), ...reportSources] }\n : {}),\n };\n };\n\n const runPhase = async (\n phase: DocumentNodePhase,\n results: StepRunResult[],\n executionSignal = signal,\n ): Promise<void> => {\n for (const [stepIndex, step] of document.lifecycle[phase].entries()) {\n const waitForPreviousOperationMs =\n stepIndex > 0\n ? await recoverCancelledScopeResources(executionSignal)\n : 0;\n const documentContext: NodeDocumentContext = {\n documentId: document.documentId,\n documentRunId,\n projectId: document.projectId,\n projectName: project.name,\n sourcePath: document.sourcePath,\n phase,\n stepIndex,\n };\n const stepInfo = {\n scope: 'document' as const,\n node: step.node,\n stepCount: document.lifecycle[phase].length,\n document: documentContext,\n };\n await options.onStepStart?.(stepInfo);\n const result = await executeStep(\n step,\n nodes[phase][stepIndex],\n { scope: 'document', document: documentContext },\n context,\n {\n signal: executionSignal,\n defaultTimeoutMs: options.defaultTimeoutMs,\n onTeardown(node, teardown) {\n if (!acceptingNodeTeardowns) {\n throw new WorkflowLifecycleError(\n 'Node teardown can only be registered while a workflow document is running.',\n { documentId: document.documentId, documentRunId, node },\n );\n }\n if (typeof teardown !== 'function') {\n throw new WorkflowLifecycleError(\n 'Node onTeardown() requires a teardown function.',\n { documentId: document.documentId, documentRunId, node },\n );\n }\n nodeTeardownStack.push({\n registrationIndex: nodeTeardownStack.length,\n node,\n teardown,\n });\n },\n },\n );\n if (waitForPreviousOperationMs > 0)\n result.waitForPreviousOperationMs = waitForPreviousOperationMs;\n results.push(result);\n for (const source of result.report?.sources ?? [])\n capturedSources.set(\n `${source.sourcePath}\\u0000${source.dumpPath ?? ''}`,\n source,\n );\n await options.onStepResult?.(stepInfo, result);\n if (result.status !== 'success' && !result.continuedAfterError) break;\n }\n };\n\n return {\n signal,\n get context() {\n return context;\n },\n get canRunCases() {\n return (\n started &&\n executionErrors.length === 0 &&\n hostErrors.length === 0 &&\n !signal.aborted &&\n !beforeAll.some((step) => step.status !== 'success')\n );\n },\n async start() {\n if (started) {\n throw new WorkflowLifecycleError(\n 'Workflow document runtime has already started.',\n { documentId: document.documentId, documentRunId },\n );\n }\n started = true;\n if (!signal.aborted && options.documentSetup) {\n acceptingSetupTeardowns = true;\n try {\n await runSetupWithTimeout(\n async () => {\n try {\n context = await options.documentSetup!.setup({\n project,\n projectContext: options.projectContext,\n document: {\n documentId: document.documentId,\n documentRunId,\n sourcePath: document.sourcePath,\n attemptIndex: options.documentAttemptIndex ?? 0,\n },\n signal,\n onTeardown(teardown) {\n if (\n !acceptingSetupTeardowns ||\n typeof teardown !== 'function'\n )\n throw new WorkflowLifecycleError(\n 'Document setup onTeardown() requires a function registered during setup.',\n { documentId: document.documentId, documentRunId },\n );\n nodeTeardownStack.push({\n registrationIndex: nodeTeardownStack.length,\n node: options.documentSetup!.name,\n teardown,\n host: true,\n });\n },\n });\n } finally {\n acceptingSetupTeardowns = false;\n }\n },\n signal,\n resourceScope.abort,\n options.setupTimeoutMs,\n );\n } catch (error) {\n hostErrors.push({ phase: 'setup', error: asExecutionError(error) });\n }\n }\n try {\n if (!signal.aborted && !hostErrors.length)\n await runPhase('beforeAll', beforeAll);\n } catch (error) {\n executionErrors.push(error);\n throw new WorkflowExecutionFailure(createResult(), [\n ...executionErrors,\n ]);\n }\n return createResult();\n },\n async finish() {\n if (finishPromise) return finishPromise;\n if (!started) {\n throw new WorkflowLifecycleError(\n 'Workflow document runtime must start before it can finish.',\n { documentId: document.documentId, documentRunId },\n );\n }\n finishPromise = (async () => {\n const teardownErrors: WorkflowError[] = [];\n const cleanupExecutionErrors: unknown[] = [];\n let deferredCleanup = false;\n const cleanup = async () => {\n const priorTeardownErrors = teardownErrors.length;\n acceptingNodeTeardowns = false;\n for (const {\n registrationIndex,\n node,\n teardown,\n host,\n } of nodeTeardownStack.reverse()) {\n try {\n const released = await teardown();\n reportSources.push(...(released?.reportSources ?? []));\n for (const path of reportPathsFromTeardown(released)) {\n reportPaths.add(path);\n }\n } catch (error) {\n if (host) {\n const failure = asExecutionError(error);\n hostErrors.push({\n phase: 'cleanup',\n error: failure,\n });\n executionErrors.push(failure);\n cleanupExecutionErrors.push(error);\n continue;\n }\n teardownErrors.push(\n new NodeScopeTeardownError(error, {\n scope: 'document',\n scopeId: documentRunId,\n node,\n registrationIndex,\n }),\n );\n }\n }\n {\n const errors = [\n ...cleanupExecutionErrors,\n ...teardownErrors.slice(priorTeardownErrors),\n ];\n if (errors.length)\n throw new AggregateError(errors, 'Document cleanup failed');\n }\n };\n try {\n if (\n document.lifecycle.afterAll.length ||\n nodeTeardownStack.length ||\n (options.setupTimeoutMs !== undefined &&\n getPendingScopeOperations(signal).length)\n )\n await cleanupScopeResources(signal, cleanup, {\n totalTimeoutMs: options.teardownTimeoutMs?.(),\n beforeCleanup: async (cleanupSignal) => {\n try {\n if (!hostErrors.some((error) => error.phase === 'setup'))\n await runPhase('afterAll', afterAll, cleanupSignal);\n } catch (error) {\n executionErrors.push(error);\n cleanupExecutionErrors.push(error);\n }\n },\n onDeferredError: (error) =>\n getDebug('runner:cleanup', { console: true })(\n `Deferred document cleanup failed: ${String(error)}`,\n ),\n });\n else acceptingNodeTeardowns = false;\n } catch (error) {\n deferredCleanup = error instanceof ResourceCleanupDeferredError;\n if (!deferredCleanup) acceptingNodeTeardowns = false;\n if (\n !deferredCleanup &&\n !teardownErrors.length &&\n !cleanupExecutionErrors.length\n )\n teardownErrors.push(\n new NodeScopeTeardownError(error, {\n scope: 'document',\n scopeId: documentRunId,\n node: 'resource-drain',\n registrationIndex: -1,\n }),\n );\n }\n finishedResult = createResult(teardownErrors);\n try {\n await options.onResult?.(finishedResult);\n } catch (error) {\n executionErrors.push(error);\n finishedResult = createResult(teardownErrors);\n }\n resourceScope.dispose();\n if (executionErrors.length)\n throw new WorkflowExecutionFailure(finishedResult, [\n ...executionErrors,\n ]);\n return finishedResult;\n })();\n return finishPromise;\n },\n };\n}\n"],"names":["createDocumentRuntime","document","options","documentRunId","randomUUID","plan","buildTestRunReportPlan","startedAt","Date","beforeAll","afterAll","nodes","step","project","Object","context","resourceScope","createResourceScope","signal","setResourceOperationIdentity","nodeTeardownStack","acceptingNodeTeardowns","started","finishedResult","finishPromise","reportPaths","Set","reportSources","capturedSources","Map","executionErrors","hostErrors","acceptingSetupTeardowns","createResult","teardownErrors","endedAt","cleanup","getResourceCleanupSnapshot","pendingOperations","getPendingScopeOperations","phaseResults","phaseResult","undefined","phase","Math","error","host","asExecutionError","runPhase","results","executionSignal","stepIndex","waitForPreviousOperationMs","recoverCancelledScopeResources","documentContext","stepInfo","result","executeStep","node","teardown","WorkflowLifecycleError","source","runSetupWithTimeout","WorkflowExecutionFailure","cleanupExecutionErrors","deferredCleanup","priorTeardownErrors","registrationIndex","released","path","reportPathsFromTeardown","failure","NodeScopeTeardownError","errors","AggregateError","cleanupScopeResources","cleanupSignal","getDebug","String","ResourceCleanupDeferredError"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getDebug } from "@midscene/shared/logger";
|
|
2
2
|
import { ProjectSetupError, ProjectTeardownError, WorkflowLifecycleError } from "../errors.mjs";
|
|
3
3
|
import { ResourceCleanupDeferredError, cleanupScopeResources, getPendingScopeOperations, getResourceCleanupSnapshot, linkResourceSignal, setResourceOperationIdentity } from "./resource-operations.mjs";
|
|
4
|
+
import { runSetupWithTimeout } from "./setup-timeout.mjs";
|
|
4
5
|
const createProjectRuntime = (options)=>{
|
|
5
6
|
const startedAt = new Date();
|
|
6
7
|
const controller = new AbortController();
|
|
@@ -77,20 +78,24 @@ const createProjectRuntime = (options)=>{
|
|
|
77
78
|
return createResult('failed');
|
|
78
79
|
}
|
|
79
80
|
if (options.setup) try {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
await runSetupWithTimeout(async ()=>{
|
|
82
|
+
try {
|
|
83
|
+
context = await options.setup.setup({
|
|
84
|
+
project: options.project,
|
|
85
|
+
env: Object.freeze({
|
|
86
|
+
...process.env
|
|
87
|
+
}),
|
|
88
|
+
signal: controller.signal,
|
|
89
|
+
onTeardown
|
|
90
|
+
});
|
|
91
|
+
} finally{
|
|
92
|
+
acceptingTeardowns = false;
|
|
93
|
+
}
|
|
94
|
+
}, controller.signal, (reason)=>controller.abort(reason), options.setupTimeoutMs);
|
|
88
95
|
} catch (error) {
|
|
89
96
|
setupError = new ProjectSetupError(error, {
|
|
90
97
|
projectName: options.project.name
|
|
91
98
|
});
|
|
92
|
-
} finally{
|
|
93
|
-
acceptingTeardowns = false;
|
|
94
99
|
}
|
|
95
100
|
return createResult(setupError ? 'failed' : 'success');
|
|
96
101
|
},
|
|
@@ -123,7 +128,8 @@ const createProjectRuntime = (options)=>{
|
|
|
123
128
|
if (teardownErrors.length > priorErrors) throw new AggregateError(teardownErrors.slice(priorErrors), 'Project cleanup failed');
|
|
124
129
|
};
|
|
125
130
|
try {
|
|
126
|
-
if (teardownStack.length) await cleanupScopeResources(controller.signal, cleanup, {
|
|
131
|
+
if (teardownStack.length || void 0 !== options.setupTimeoutMs && getPendingScopeOperations(controller.signal).length) await cleanupScopeResources(controller.signal, cleanup, {
|
|
132
|
+
totalTimeoutMs: options.teardownTimeoutMs?.(),
|
|
127
133
|
onDeferredError: (error)=>getDebug('runner:cleanup', {
|
|
128
134
|
console: true
|
|
129
135
|
})(`Deferred Project cleanup failed: ${String(error)}`)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-runner/engine/project-runtime.mjs","sources":["../../../../src/test-runner/engine/project-runtime.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport {\n ProjectSetupError,\n ProjectTeardownError,\n WorkflowLifecycleError,\n} from '../errors';\nimport {\n ResourceCleanupDeferredError,\n cleanupScopeResources,\n getPendingScopeOperations,\n getResourceCleanupSnapshot,\n linkResourceSignal,\n setResourceOperationIdentity,\n} from './resource-operations';\nimport type {\n ProjectRuntime,\n ProjectRuntimeOptions,\n ProjectRuntimeResult,\n} from './types';\n\nexport const createProjectRuntime = <TProjectContext = unknown>(\n options: ProjectRuntimeOptions<TProjectContext>,\n): ProjectRuntime<TProjectContext> => {\n const startedAt = new Date();\n const controller = new AbortController();\n const parentSignal = options.signal;\n linkResourceSignal(controller.signal, parentSignal);\n setResourceOperationIdentity(controller.signal, {\n scopeId: options.project.projectId,\n });\n const abortFromParent = () => controller.abort(parentSignal?.reason);\n if (parentSignal?.aborted) abortFromParent();\n else parentSignal?.addEventListener('abort', abortFromParent, { once: true });\n\n const teardownStack: Array<{\n registrationIndex: number;\n teardown: Parameters<\n Parameters<NonNullable<typeof options.setup>['setup']>[0]['onTeardown']\n >[0];\n }> = [];\n let context: TProjectContext | undefined;\n let setupError: ProjectSetupError | undefined;\n let started = false;\n let acceptingTeardowns = options.setup !== undefined;\n let finishedResult: ProjectRuntimeResult<TProjectContext> | undefined;\n let finishPromise: Promise<ProjectRuntimeResult<TProjectContext>> | undefined;\n\n const createResult = (\n requestedStatus: 'success' | 'failed',\n teardownErrors: readonly ProjectTeardownError[] = [],\n ): ProjectRuntimeResult<TProjectContext> => {\n const endedAt = new Date();\n const cleanup = getResourceCleanupSnapshot(controller.signal);\n const pendingOperations = getPendingScopeOperations(controller.signal);\n return {\n projectName: options.project.name,\n status:\n requestedStatus === 'failed' ||\n setupError ||\n teardownErrors.length > 0 ||\n cleanup.status === 'pending' ||\n cleanup.status === 'failed' ||\n pendingOperations.length > 0\n ? 'failed'\n : 'success',\n ...(setupError ? { setupError } : {}),\n ...(teardownErrors.length > 0\n ? { teardownErrors: [...teardownErrors] }\n : {}),\n startedAt: startedAt.toISOString(),\n endedAt: endedAt.toISOString(),\n durationMs: Math.max(0, endedAt.getTime() - startedAt.getTime()),\n cleanup,\n ...(pendingOperations.length ? { pendingOperations } : {}),\n };\n };\n\n const onTeardown = (\n teardown: (typeof teardownStack)[number]['teardown'],\n ): void => {\n if (!acceptingTeardowns) {\n throw new WorkflowLifecycleError(\n 'onTeardown() can only be called while Project setup is running.',\n { projectName: options.project.name },\n );\n }\n if (typeof teardown !== 'function') {\n throw new WorkflowLifecycleError(\n 'Project onTeardown() requires a teardown function.',\n { projectName: options.project.name },\n );\n }\n teardownStack.push({\n registrationIndex: teardownStack.length,\n teardown,\n });\n };\n\n return {\n get context() {\n return context;\n },\n get signal() {\n return controller.signal;\n },\n get canRun() {\n return started && !setupError && !controller.signal.aborted;\n },\n async start() {\n if (started) {\n throw new WorkflowLifecycleError(\n 'Project runtime has already started.',\n {\n projectName: options.project.name,\n },\n );\n }\n started = true;\n if (controller.signal.aborted) {\n acceptingTeardowns = false;\n return createResult('failed');\n }\n if (options.setup) {\n try {\n context = await options.setup.setup({\n project: options.project,\n env: Object.freeze({ ...process.env }),\n signal: controller.signal,\n onTeardown,\n });\n } catch (error) {\n setupError = new ProjectSetupError(error, {\n projectName: options.project.name,\n });\n } finally {\n acceptingTeardowns = false;\n }\n }\n return createResult(setupError ? 'failed' : 'success');\n },\n async finish(requestedStatus = 'success') {\n if (finishPromise) return finishPromise;\n if (!started) {\n throw new WorkflowLifecycleError(\n 'Project runtime must start before it can finish.',\n { projectName: options.project.name },\n );\n }\n finishPromise = (async () => {\n const statusBeforeTeardown = createResult(requestedStatus).status;\n const teardownErrors: ProjectTeardownError[] = [];\n let deferredCleanup = false;\n const cleanup = async () => {\n const priorErrors = teardownErrors.length;\n for (const {\n registrationIndex,\n teardown,\n } of teardownStack.reverse()) {\n try {\n await teardown({\n project: options.project,\n context,\n status: statusBeforeTeardown,\n ...(setupError ? { setupError } : {}),\n });\n } catch (error) {\n teardownErrors.push(\n new ProjectTeardownError(error, {\n projectName: options.project.name,\n registrationIndex,\n }),\n );\n }\n }\n if (teardownErrors.length > priorErrors)\n throw new AggregateError(\n teardownErrors.slice(priorErrors),\n 'Project cleanup failed',\n );\n };\n try {\n if (teardownStack.length)\n await cleanupScopeResources(controller.signal, cleanup, {\n onDeferredError: (error) =>\n getDebug('runner:cleanup', { console: true })(\n `Deferred Project cleanup failed: ${String(error)}`,\n ),\n });\n } catch (error) {\n deferredCleanup = error instanceof ResourceCleanupDeferredError;\n if (!deferredCleanup && !teardownErrors.length)\n teardownErrors.push(\n new ProjectTeardownError(error, {\n projectName: options.project.name,\n registrationIndex: -1,\n }),\n );\n }\n parentSignal?.removeEventListener('abort', abortFromParent);\n finishedResult = createResult(requestedStatus, teardownErrors);\n return finishedResult;\n })();\n return finishPromise;\n },\n abort(reason) {\n controller.abort(reason);\n },\n };\n};\n"],"names":["createProjectRuntime","options","startedAt","Date","controller","AbortController","parentSignal","linkResourceSignal","setResourceOperationIdentity","abortFromParent","teardownStack","context","setupError","started","acceptingTeardowns","undefined","finishedResult","finishPromise","createResult","requestedStatus","teardownErrors","endedAt","cleanup","getResourceCleanupSnapshot","pendingOperations","getPendingScopeOperations","Math","onTeardown","teardown","WorkflowLifecycleError","Object","process","error","ProjectSetupError","statusBeforeTeardown","deferredCleanup","priorErrors","registrationIndex","ProjectTeardownError","AggregateError","cleanupScopeResources","getDebug","String","ResourceCleanupDeferredError","reason"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"test-runner/engine/project-runtime.mjs","sources":["../../../../src/test-runner/engine/project-runtime.ts"],"sourcesContent":["import { getDebug } from '@midscene/shared/logger';\nimport {\n ProjectSetupError,\n ProjectTeardownError,\n WorkflowLifecycleError,\n} from '../errors';\nimport {\n ResourceCleanupDeferredError,\n cleanupScopeResources,\n getPendingScopeOperations,\n getResourceCleanupSnapshot,\n linkResourceSignal,\n setResourceOperationIdentity,\n} from './resource-operations';\nimport { runSetupWithTimeout } from './setup-timeout';\nimport type {\n ProjectRuntime,\n ProjectRuntimeOptions,\n ProjectRuntimeResult,\n} from './types';\n\nexport const createProjectRuntime = <TProjectContext = unknown>(\n options: ProjectRuntimeOptions<TProjectContext>,\n): ProjectRuntime<TProjectContext> => {\n const startedAt = new Date();\n const controller = new AbortController();\n const parentSignal = options.signal;\n linkResourceSignal(controller.signal, parentSignal);\n setResourceOperationIdentity(controller.signal, {\n scopeId: options.project.projectId,\n });\n const abortFromParent = () => controller.abort(parentSignal?.reason);\n if (parentSignal?.aborted) abortFromParent();\n else parentSignal?.addEventListener('abort', abortFromParent, { once: true });\n\n const teardownStack: Array<{\n registrationIndex: number;\n teardown: Parameters<\n Parameters<NonNullable<typeof options.setup>['setup']>[0]['onTeardown']\n >[0];\n }> = [];\n let context: TProjectContext | undefined;\n let setupError: ProjectSetupError | undefined;\n let started = false;\n let acceptingTeardowns = options.setup !== undefined;\n let finishedResult: ProjectRuntimeResult<TProjectContext> | undefined;\n let finishPromise: Promise<ProjectRuntimeResult<TProjectContext>> | undefined;\n\n const createResult = (\n requestedStatus: 'success' | 'failed',\n teardownErrors: readonly ProjectTeardownError[] = [],\n ): ProjectRuntimeResult<TProjectContext> => {\n const endedAt = new Date();\n const cleanup = getResourceCleanupSnapshot(controller.signal);\n const pendingOperations = getPendingScopeOperations(controller.signal);\n return {\n projectName: options.project.name,\n status:\n requestedStatus === 'failed' ||\n setupError ||\n teardownErrors.length > 0 ||\n cleanup.status === 'pending' ||\n cleanup.status === 'failed' ||\n pendingOperations.length > 0\n ? 'failed'\n : 'success',\n ...(setupError ? { setupError } : {}),\n ...(teardownErrors.length > 0\n ? { teardownErrors: [...teardownErrors] }\n : {}),\n startedAt: startedAt.toISOString(),\n endedAt: endedAt.toISOString(),\n durationMs: Math.max(0, endedAt.getTime() - startedAt.getTime()),\n cleanup,\n ...(pendingOperations.length ? { pendingOperations } : {}),\n };\n };\n\n const onTeardown = (\n teardown: (typeof teardownStack)[number]['teardown'],\n ): void => {\n if (!acceptingTeardowns) {\n throw new WorkflowLifecycleError(\n 'onTeardown() can only be called while Project setup is running.',\n { projectName: options.project.name },\n );\n }\n if (typeof teardown !== 'function') {\n throw new WorkflowLifecycleError(\n 'Project onTeardown() requires a teardown function.',\n { projectName: options.project.name },\n );\n }\n teardownStack.push({\n registrationIndex: teardownStack.length,\n teardown,\n });\n };\n\n return {\n get context() {\n return context;\n },\n get signal() {\n return controller.signal;\n },\n get canRun() {\n return started && !setupError && !controller.signal.aborted;\n },\n async start() {\n if (started) {\n throw new WorkflowLifecycleError(\n 'Project runtime has already started.',\n {\n projectName: options.project.name,\n },\n );\n }\n started = true;\n if (controller.signal.aborted) {\n acceptingTeardowns = false;\n return createResult('failed');\n }\n if (options.setup) {\n try {\n await runSetupWithTimeout(\n async () => {\n try {\n context = await options.setup!.setup({\n project: options.project,\n env: Object.freeze({ ...process.env }),\n signal: controller.signal,\n onTeardown,\n });\n } finally {\n acceptingTeardowns = false;\n }\n },\n controller.signal,\n (reason) => controller.abort(reason),\n options.setupTimeoutMs,\n );\n } catch (error) {\n setupError = new ProjectSetupError(error, {\n projectName: options.project.name,\n });\n }\n }\n return createResult(setupError ? 'failed' : 'success');\n },\n async finish(requestedStatus = 'success') {\n if (finishPromise) return finishPromise;\n if (!started) {\n throw new WorkflowLifecycleError(\n 'Project runtime must start before it can finish.',\n { projectName: options.project.name },\n );\n }\n finishPromise = (async () => {\n const statusBeforeTeardown = createResult(requestedStatus).status;\n const teardownErrors: ProjectTeardownError[] = [];\n let deferredCleanup = false;\n const cleanup = async () => {\n const priorErrors = teardownErrors.length;\n for (const {\n registrationIndex,\n teardown,\n } of teardownStack.reverse()) {\n try {\n await teardown({\n project: options.project,\n context,\n status: statusBeforeTeardown,\n ...(setupError ? { setupError } : {}),\n });\n } catch (error) {\n teardownErrors.push(\n new ProjectTeardownError(error, {\n projectName: options.project.name,\n registrationIndex,\n }),\n );\n }\n }\n if (teardownErrors.length > priorErrors)\n throw new AggregateError(\n teardownErrors.slice(priorErrors),\n 'Project cleanup failed',\n );\n };\n try {\n if (\n teardownStack.length ||\n (options.setupTimeoutMs !== undefined &&\n getPendingScopeOperations(controller.signal).length)\n )\n await cleanupScopeResources(controller.signal, cleanup, {\n totalTimeoutMs: options.teardownTimeoutMs?.(),\n onDeferredError: (error) =>\n getDebug('runner:cleanup', { console: true })(\n `Deferred Project cleanup failed: ${String(error)}`,\n ),\n });\n } catch (error) {\n deferredCleanup = error instanceof ResourceCleanupDeferredError;\n if (!deferredCleanup && !teardownErrors.length)\n teardownErrors.push(\n new ProjectTeardownError(error, {\n projectName: options.project.name,\n registrationIndex: -1,\n }),\n );\n }\n parentSignal?.removeEventListener('abort', abortFromParent);\n finishedResult = createResult(requestedStatus, teardownErrors);\n return finishedResult;\n })();\n return finishPromise;\n },\n abort(reason) {\n controller.abort(reason);\n },\n };\n};\n"],"names":["createProjectRuntime","options","startedAt","Date","controller","AbortController","parentSignal","linkResourceSignal","setResourceOperationIdentity","abortFromParent","teardownStack","context","setupError","started","acceptingTeardowns","undefined","finishedResult","finishPromise","createResult","requestedStatus","teardownErrors","endedAt","cleanup","getResourceCleanupSnapshot","pendingOperations","getPendingScopeOperations","Math","onTeardown","teardown","WorkflowLifecycleError","runSetupWithTimeout","Object","process","reason","error","ProjectSetupError","statusBeforeTeardown","deferredCleanup","priorErrors","registrationIndex","ProjectTeardownError","AggregateError","cleanupScopeResources","getDebug","String","ResourceCleanupDeferredError"],"mappings":"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"}
|
|
@@ -105,6 +105,9 @@ function createResourceScope(parent) {
|
|
|
105
105
|
});
|
|
106
106
|
return {
|
|
107
107
|
signal: controller.signal,
|
|
108
|
+
abort (reason) {
|
|
109
|
+
controller.abort(reason);
|
|
110
|
+
},
|
|
108
111
|
dispose () {
|
|
109
112
|
parent?.removeEventListener('abort', abort);
|
|
110
113
|
scope.resources = void 0;
|
|
@@ -324,6 +327,15 @@ async function waitForResourceIdle(resource) {
|
|
|
324
327
|
...resource_operations_resources.get(resource).operations.keys()
|
|
325
328
|
]);
|
|
326
329
|
}
|
|
330
|
+
async function waitForScopeOperations(signal) {
|
|
331
|
+
while(true){
|
|
332
|
+
const active = [
|
|
333
|
+
...scopes.get(signal)?.operations.keys() ?? []
|
|
334
|
+
];
|
|
335
|
+
if (!active.length) return;
|
|
336
|
+
await Promise.all(active.map(waitForResourceIdle));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
327
339
|
async function recoverCancelledScopeResources(signal, graceMs = RESOURCE_RECOVERY_GRACE_MS) {
|
|
328
340
|
signal.throwIfAborted();
|
|
329
341
|
const startedAt = Date.now();
|
|
@@ -409,14 +421,22 @@ async function runResourceCleanup(resources, cleanup, options, beforeCleanup) {
|
|
|
409
421
|
if (ownerScope) ownerScope.cleanupOwner = owner;
|
|
410
422
|
let defer;
|
|
411
423
|
const deferred = new Promise((resolve)=>{
|
|
412
|
-
defer = ()=>
|
|
424
|
+
defer = ()=>{
|
|
425
|
+
if (void 0 !== options.totalTimeoutMs) beforeCleanup?.abort(new WorkflowError(`Resource cleanup timed out after ${options.totalTimeoutMs}ms.`, {
|
|
426
|
+
code: 'TEARDOWN_TIMEOUT',
|
|
427
|
+
details: {
|
|
428
|
+
timeoutMs: options.totalTimeoutMs
|
|
429
|
+
}
|
|
430
|
+
}));
|
|
431
|
+
resolve({
|
|
413
432
|
deferred: true
|
|
414
433
|
});
|
|
434
|
+
};
|
|
415
435
|
});
|
|
416
|
-
let graceMs = options.graceMs ?? RESOURCE_CLEANUP_GRACE_MS;
|
|
436
|
+
let graceMs = options.totalTimeoutMs ?? options.graceMs ?? RESOURCE_CLEANUP_GRACE_MS;
|
|
417
437
|
let timer;
|
|
418
438
|
const drain = async ()=>{
|
|
419
|
-
timer = setTimeout(defer, graceMs);
|
|
439
|
+
if (void 0 === options.totalTimeoutMs) timer = setTimeout(defer, graceMs);
|
|
420
440
|
try {
|
|
421
441
|
while(true){
|
|
422
442
|
const pending = [
|
|
@@ -426,15 +446,18 @@ async function runResourceCleanup(resources, cleanup, options, beforeCleanup) {
|
|
|
426
446
|
await Promise.all(pending.map(waitForResourceIdle));
|
|
427
447
|
}
|
|
428
448
|
} finally{
|
|
429
|
-
clearTimeout(timer);
|
|
449
|
+
if (void 0 === options.totalTimeoutMs) clearTimeout(timer);
|
|
430
450
|
}
|
|
431
451
|
};
|
|
432
452
|
const completion = (async ()=>{
|
|
453
|
+
if (void 0 !== options.totalTimeoutMs) timer = setTimeout(defer, Math.max(0, options.totalTimeoutMs));
|
|
433
454
|
try {
|
|
434
455
|
if (beforeCleanup) await beforeCleanup.run(beforeCleanup.signal);
|
|
435
456
|
await drain();
|
|
436
|
-
|
|
437
|
-
|
|
457
|
+
if (void 0 === options.totalTimeoutMs) {
|
|
458
|
+
graceMs = options.graceMs ?? RESOURCE_TEARDOWN_GRACE_MS;
|
|
459
|
+
timer = setTimeout(defer, graceMs);
|
|
460
|
+
}
|
|
438
461
|
return await cleanup();
|
|
439
462
|
} finally{
|
|
440
463
|
clearTimeout(timer);
|
|
@@ -488,7 +511,8 @@ function cleanupResources(resources, cleanup, options) {
|
|
|
488
511
|
return runResourceCleanup(resources, cleanup, options);
|
|
489
512
|
}
|
|
490
513
|
async function cleanupScopeResources(signal, cleanup, options) {
|
|
491
|
-
const
|
|
514
|
+
const cleanupController = new AbortController();
|
|
515
|
+
const cleanupSignal = cleanupController.signal;
|
|
492
516
|
linkResourceSignal(cleanupSignal, signal);
|
|
493
517
|
const scope = scopes.get(signal);
|
|
494
518
|
return runResourceCleanup([
|
|
@@ -500,9 +524,10 @@ async function cleanupScopeResources(signal, cleanup, options) {
|
|
|
500
524
|
signal
|
|
501
525
|
}, options.beforeCleanup ? {
|
|
502
526
|
signal: cleanupSignal,
|
|
527
|
+
abort: (reason)=>cleanupController.abort(reason),
|
|
503
528
|
run: options.beforeCleanup
|
|
504
529
|
} : void 0);
|
|
505
530
|
}
|
|
506
|
-
export { RESOURCE_CLEANUP_GRACE_MS, RESOURCE_RECOVERY_GRACE_MS, ResourceAdmissionError, ResourceCleanupDeferredError, admitResourceOperation, assertResourceAvailable, cleanupResources, cleanupScopeResources, createResourceScope, getPendingScopeOperations, getResourceCleanupCompletion, getResourceCleanupSnapshot, hasPendingResourceOperation, isResourceAdmissionError, linkResourceSignal, observeResourceOperations, recordOperation, recoverCancelledScopeResources, retainResourceForScope, setResourceOperationIdentity, trackResourceOperation, waitForResourceIdle };
|
|
531
|
+
export { RESOURCE_CLEANUP_GRACE_MS, RESOURCE_RECOVERY_GRACE_MS, ResourceAdmissionError, ResourceCleanupDeferredError, admitResourceOperation, assertResourceAvailable, cleanupResources, cleanupScopeResources, createResourceScope, getPendingScopeOperations, getResourceCleanupCompletion, getResourceCleanupSnapshot, hasPendingResourceOperation, isResourceAdmissionError, linkResourceSignal, observeResourceOperations, recordOperation, recoverCancelledScopeResources, retainResourceForScope, setResourceOperationIdentity, trackResourceOperation, waitForResourceIdle, waitForScopeOperations };
|
|
507
532
|
|
|
508
533
|
//# sourceMappingURL=resource-operations.mjs.map
|