donobu 5.52.3 → 5.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/lib/page/extendPage.js +3 -1
- package/dist/esm/lib/test/testExtension.js +98 -38
- package/dist/esm/managers/DonobuFlowsManager.js +3 -1
- package/dist/esm/managers/TestsManager.js +3 -1
- package/dist/esm/models/CreateSuite.d.ts +1 -0
- package/dist/esm/models/CreateTest.d.ts +1 -0
- package/dist/esm/models/FlowMetadata.d.ts +1 -0
- package/dist/esm/models/MetadataVersion.d.ts +14 -0
- package/dist/esm/models/MetadataVersion.js +17 -0
- package/dist/esm/models/RunConfig.d.ts +1 -0
- package/dist/esm/models/RunConfig.js +8 -0
- package/dist/esm/models/SuiteMetadata.d.ts +1 -0
- package/dist/esm/models/TestMetadata.d.ts +5 -0
- package/dist/esm/persistence/normalizeFlowMetadata.d.ts +0 -5
- package/dist/esm/persistence/normalizeFlowMetadata.js +2 -7
- package/dist/lib/page/extendPage.js +3 -1
- package/dist/lib/test/testExtension.js +98 -38
- package/dist/managers/DonobuFlowsManager.js +3 -1
- package/dist/managers/TestsManager.js +3 -1
- package/dist/models/CreateSuite.d.ts +1 -0
- package/dist/models/CreateTest.d.ts +1 -0
- package/dist/models/FlowMetadata.d.ts +1 -0
- package/dist/models/MetadataVersion.d.ts +14 -0
- package/dist/models/MetadataVersion.js +17 -0
- package/dist/models/RunConfig.d.ts +1 -0
- package/dist/models/RunConfig.js +8 -0
- package/dist/models/SuiteMetadata.d.ts +1 -0
- package/dist/models/TestMetadata.d.ts +5 -0
- package/dist/persistence/normalizeFlowMetadata.d.ts +0 -5
- package/dist/persistence/normalizeFlowMetadata.js +2 -7
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@ const InteractionVisualizer_1 = require("../../managers/InteractionVisualizer");
|
|
|
15
15
|
const ToolManager_1 = require("../../managers/ToolManager");
|
|
16
16
|
const WebTargetInspector_1 = require("../../managers/WebTargetInspector");
|
|
17
17
|
const ControlPanel_1 = require("../../models/ControlPanel");
|
|
18
|
+
const MetadataVersion_1 = require("../../models/MetadataVersion");
|
|
18
19
|
const AnalyzePageTextTool_1 = require("../../tools/AnalyzePageTextTool");
|
|
19
20
|
const AssertTool_1 = require("../../tools/AssertTool");
|
|
20
21
|
const AuditTool_1 = require("../../tools/AuditTool");
|
|
@@ -88,7 +89,7 @@ async function extendPage(page, options) {
|
|
|
88
89
|
const sharedState = {
|
|
89
90
|
donobuFlowMetadata: {
|
|
90
91
|
id: options?.flowId ?? (0, crypto_1.randomUUID)(),
|
|
91
|
-
metadataVersion:
|
|
92
|
+
metadataVersion: MetadataVersion_1.CURRENT_METADATA_VERSION,
|
|
92
93
|
name: null,
|
|
93
94
|
createdWithDonobuVersion: MiscUtils_1.MiscUtils.DONOBU_VERSION,
|
|
94
95
|
target: 'web',
|
|
@@ -104,6 +105,7 @@ async function extendPage(page, options) {
|
|
|
104
105
|
targetWebsite: PLACEHOLDER_FLOW_URL,
|
|
105
106
|
},
|
|
106
107
|
envVars: options?.envVars ?? null,
|
|
108
|
+
tags: [],
|
|
107
109
|
gptConfigName: null,
|
|
108
110
|
hasGptConfigNameOverride: false,
|
|
109
111
|
customTools: null,
|
|
@@ -409,6 +409,7 @@ exports.test = test_1.test.extend({
|
|
|
409
409
|
extendedPage._dnb.donobuFlowMetadata.name = getSanitizedTestName(testInfo);
|
|
410
410
|
extendedPage._dnb.donobuFlowMetadata.testId = testInfo.testId;
|
|
411
411
|
extendedPage._dnb.donobuFlowMetadata.overallObjective = overallObjective;
|
|
412
|
+
extendedPage._dnb.donobuFlowMetadata.tags = testInfo.tags;
|
|
412
413
|
// Register browser console and network listeners so that logs from these
|
|
413
414
|
// sources are captured into the flow's logBuffer. In Studio-launched flows
|
|
414
415
|
// this is done by WebTargetInspector.initialize(), but that method is not
|
|
@@ -421,12 +422,27 @@ exports.test = test_1.test.extend({
|
|
|
421
422
|
const asyncScope = new async_hooks_1.AsyncResource('DonobuTestContext');
|
|
422
423
|
const boundUse = asyncScope.bind(use);
|
|
423
424
|
try {
|
|
425
|
+
const testMetadata = flowMetadataToTestMetadata(testInfo.testId, extendedPage._dnb.donobuFlowMetadata);
|
|
424
426
|
try {
|
|
425
|
-
const testMetadata = flowMetadataToTestMetadata(testInfo.testId, extendedPage._dnb.donobuFlowMetadata);
|
|
426
427
|
await extendedPage._dnb.testsPersistence.createTest(testMetadata);
|
|
427
428
|
}
|
|
428
429
|
catch {
|
|
429
|
-
// Test already exists from a prior run
|
|
430
|
+
// Test already exists from a prior run. Refresh the fields the SDK
|
|
431
|
+
// is the source of truth for so changes in the test code (e.g. a
|
|
432
|
+
// new `@tag`, an updated overallObjective annotation, additional
|
|
433
|
+
// `ENV` annotations, a different target URL) flow through to the
|
|
434
|
+
// persisted record — while leaving Studio-owned fields like
|
|
435
|
+
// `suiteId` and `nextRunMode` untouched.
|
|
436
|
+
const existing = await extendedPage._dnb.testsPersistence.getTestById(testInfo.testId);
|
|
437
|
+
await extendedPage._dnb.testsPersistence.updateTest({
|
|
438
|
+
...existing,
|
|
439
|
+
provenance: testMetadata.provenance,
|
|
440
|
+
web: testMetadata.web,
|
|
441
|
+
envVars: testMetadata.envVars,
|
|
442
|
+
overallObjective: testMetadata.overallObjective,
|
|
443
|
+
allowedTools: testMetadata.allowedTools,
|
|
444
|
+
tags: testMetadata.tags,
|
|
445
|
+
});
|
|
430
446
|
}
|
|
431
447
|
await extendedPage._dnb.persistence.setFlowMetadata(extendedPage._dnb.donobuFlowMetadata);
|
|
432
448
|
await boundUse(extendedPage);
|
|
@@ -846,6 +862,43 @@ async function attachStepScreenshots(sharedState, testInfo) {
|
|
|
846
862
|
contentType: 'application/json',
|
|
847
863
|
});
|
|
848
864
|
}
|
|
865
|
+
/**
|
|
866
|
+
* Build the per-flow `donobu-test-result.json` payload the website renders as
|
|
867
|
+
* the report's summary row + error/skip sections: the test-level scalars
|
|
868
|
+
* (status, objective, annotations, assertion errors).
|
|
869
|
+
*/
|
|
870
|
+
function buildDonobuTestResult(testInfo) {
|
|
871
|
+
const objective = testInfo.annotations.find((a) => a.type === 'objective')?.description ??
|
|
872
|
+
null;
|
|
873
|
+
return {
|
|
874
|
+
status: testInfo.status ?? 'unknown',
|
|
875
|
+
specTitle: testInfo.title,
|
|
876
|
+
file: path_1.default.relative(testInfo.config.rootDir, testInfo.file),
|
|
877
|
+
projectName: testInfo.project.name,
|
|
878
|
+
durationMs: testInfo.duration,
|
|
879
|
+
objective,
|
|
880
|
+
annotations: testInfo.annotations,
|
|
881
|
+
// TestInfoError exposes only message/stack at runtime; the assertion diff
|
|
882
|
+
// (expected/received) is embedded in the message for expect() failures, and
|
|
883
|
+
// the stack carries the code frame — enough for the website's error block.
|
|
884
|
+
errors: (testInfo.errors ?? []).map((e) => ({
|
|
885
|
+
message: e.message,
|
|
886
|
+
stack: e.stack,
|
|
887
|
+
})),
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Upload a per-flow JSON artifact through the flow's persistence layer.
|
|
892
|
+
* Best-effort: a failed upload must never affect the test result.
|
|
893
|
+
*/
|
|
894
|
+
async function persistFlowJson(persistence, flowId, fileId, value) {
|
|
895
|
+
try {
|
|
896
|
+
await persistence.setFlowFile(flowId, fileId, Buffer.from(JSON.stringify(value), 'utf8'));
|
|
897
|
+
}
|
|
898
|
+
catch (error) {
|
|
899
|
+
Logger_1.appLogger.warn(`Failed to persist report artifact "${fileId}" for flow ${flowId}: ${error.message}`);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
849
902
|
/**
|
|
850
903
|
* Capture a live screenshot of the flow's final visual state at teardown (page
|
|
851
904
|
* still open) and persist it as a per-flow file — the single source of truth
|
|
@@ -855,14 +908,14 @@ async function attachStepScreenshots(sharedState, testInfo) {
|
|
|
855
908
|
* See `fetchBaselineScreenshot` / `gatherTestFailureEvidence` in
|
|
856
909
|
* triageTestFailure.ts.
|
|
857
910
|
*
|
|
858
|
-
* Runs for any meaningful end state
|
|
859
|
-
*
|
|
860
|
-
*
|
|
911
|
+
* Runs for any meaningful end state, including V1 (objective-annotated) tests;
|
|
912
|
+
* skipped only for `skipped` tests (no real page state) or when triage is
|
|
913
|
+
* disabled. Triage reads this screenshot as the current run's failure shot and
|
|
914
|
+
* as the baseline for a later failing run. Best-effort and fails open.
|
|
861
915
|
*/
|
|
862
916
|
async function captureAndPersistFinalState(page, testInfo) {
|
|
863
917
|
if (testInfo.status === 'skipped' ||
|
|
864
|
-
process.env.DONOBU_TRIAGE_DISABLED === '1'
|
|
865
|
-
isV1Test(testInfo)) {
|
|
918
|
+
process.env.DONOBU_TRIAGE_DISABLED === '1') {
|
|
866
919
|
return;
|
|
867
920
|
}
|
|
868
921
|
const flowId = page._dnb?.donobuFlowMetadata?.id;
|
|
@@ -902,16 +955,15 @@ async function finalizeTest(page, testInfo, logBuffer, videoOption) {
|
|
|
902
955
|
body: JSON.stringify(sharedState.donobuFlowMetadata, null, 2),
|
|
903
956
|
contentType: 'application/json',
|
|
904
957
|
});
|
|
958
|
+
// Persist the per-flow test result (status, objective, annotations, and
|
|
959
|
+
// assertion errors with snippets/diffs) so the website can render the
|
|
960
|
+
// report's summary row + error section. Playwright's TestInfoError already
|
|
961
|
+
// carries the code `snippet`, so no source-file access is needed.
|
|
962
|
+
await persistFlowJson(sharedState.persistence, sharedState.donobuFlowMetadata.id, 'donobu-test-result.json', buildDonobuTestResult(testInfo));
|
|
905
963
|
// Persist captured flow logs so they are available in the Donobu UI,
|
|
906
964
|
// mirroring what DonobuFlowsManager does for Studio-launched flows.
|
|
907
965
|
if (logBuffer) {
|
|
908
|
-
|
|
909
|
-
const snapshot = logBuffer.snapshot();
|
|
910
|
-
await sharedState.persistence.setFlowFile(sharedState.donobuFlowMetadata.id, 'logs.json', Buffer.from(JSON.stringify(snapshot)));
|
|
911
|
-
}
|
|
912
|
-
catch (error) {
|
|
913
|
-
Logger_1.appLogger.error('Failed to persist flow logs:', error);
|
|
914
|
-
}
|
|
966
|
+
await persistFlowJson(sharedState.persistence, sharedState.donobuFlowMetadata.id, 'logs.json', logBuffer.snapshot());
|
|
915
967
|
}
|
|
916
968
|
// Attach step-level screenshots from the flow's tool call history.
|
|
917
969
|
// These enable the HTML report to show a visual timeline of what the
|
|
@@ -927,6 +979,7 @@ async function finalizeTest(page, testInfo, logBuffer, videoOption) {
|
|
|
927
979
|
body: JSON.stringify(nativeSteps),
|
|
928
980
|
contentType: 'application/json',
|
|
929
981
|
});
|
|
982
|
+
await persistFlowJson(sharedState.persistence, sharedState.donobuFlowMetadata.id, 'donobu-native-steps.json', nativeSteps);
|
|
930
983
|
}
|
|
931
984
|
}
|
|
932
985
|
catch {
|
|
@@ -945,6 +998,7 @@ async function finalizeTest(page, testInfo, logBuffer, videoOption) {
|
|
|
945
998
|
body: JSON.stringify(aiInvocations),
|
|
946
999
|
contentType: 'application/json',
|
|
947
1000
|
});
|
|
1001
|
+
await persistFlowJson(sharedState.persistence, sharedState.donobuFlowMetadata.id, 'donobu-ai-invocations.json', aiInvocations);
|
|
948
1002
|
}
|
|
949
1003
|
}
|
|
950
1004
|
catch {
|
|
@@ -961,35 +1015,40 @@ async function finalizeTest(page, testInfo, logBuffer, videoOption) {
|
|
|
961
1015
|
// future failing run reads a successful run's copy as its baseline.
|
|
962
1016
|
await captureAndPersistFinalState(page, testInfo);
|
|
963
1017
|
if (testInfo.status === 'failed') {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
}
|
|
977
|
-
}
|
|
1018
|
+
// Gather failure-triage evidence for every failed test, regardless of its
|
|
1019
|
+
// V1 (objective-annotated) classification or self-heal setting. Triage is a
|
|
1020
|
+
// standalone diagnostic: it writes the failure evidence that populates the
|
|
1021
|
+
// triage run directory, feeds the reports, and supplies the treatment plans
|
|
1022
|
+
// auto-heal consumes. Legacy V1 self-heal runs separately, below.
|
|
1023
|
+
try {
|
|
1024
|
+
const evidenceResult = await (0, triageTestFailure_1.gatherTestFailureEvidence)(testInfo, page);
|
|
1025
|
+
if (evidenceResult?.filePath) {
|
|
1026
|
+
Logger_1.appLogger.info(`Persisted Donobu triage evidence for "${testInfo.title}" to ${evidenceResult.filePath}.`);
|
|
1027
|
+
}
|
|
1028
|
+
else if (evidenceResult?.evidence) {
|
|
1029
|
+
Logger_1.appLogger.info(`Captured Donobu triage evidence for "${testInfo.title}" (schema v${evidenceResult.evidence.schemaVersion}).`);
|
|
978
1030
|
}
|
|
979
1031
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1032
|
+
catch (error) {
|
|
1033
|
+
Logger_1.appLogger.error(`Failed to gather test failure evidence for "${testInfo.title}".`, error);
|
|
1034
|
+
}
|
|
1035
|
+
// Legacy V1 self-heal: only for objective-annotated tests that opt in via
|
|
1036
|
+
// SELF_HEAL_TESTS_ENABLED, and never during an auto-heal rerun (which owns
|
|
1037
|
+
// its own remediation path).
|
|
1038
|
+
if (isV1Test(testInfo) &&
|
|
1039
|
+
isV1SelfHealingEnabled(testInfo) &&
|
|
1040
|
+
!MiscUtils_1.MiscUtils.yn(envVars_1.env.data.DONOBU_AUTO_HEAL_ACTIVE)) {
|
|
1041
|
+
if (!sharedState.gptClient) {
|
|
1042
|
+
Logger_1.appLogger.warn('Will not self-heal due to no GPT client being set up.');
|
|
1043
|
+
}
|
|
1044
|
+
else {
|
|
1045
|
+
try {
|
|
1046
|
+
await (0, selfHealing_1.selfHeal)(sharedState.gptClient, testInfo, page);
|
|
985
1047
|
}
|
|
986
|
-
|
|
987
|
-
Logger_1.appLogger.
|
|
1048
|
+
catch (error) {
|
|
1049
|
+
Logger_1.appLogger.error('Error when attempting to self-heal', error);
|
|
988
1050
|
}
|
|
989
1051
|
}
|
|
990
|
-
catch (error) {
|
|
991
|
-
Logger_1.appLogger.error(`Failed to gather test failure evidence for "${testInfo.title}".`, error);
|
|
992
|
-
}
|
|
993
1052
|
}
|
|
994
1053
|
}
|
|
995
1054
|
else if (testInfo.status === 'passed' &&
|
|
@@ -1062,6 +1121,7 @@ function flowMetadataToTestMetadata(testId, flowMeta) {
|
|
|
1062
1121
|
resultJsonSchema: flowMeta.resultJsonSchema,
|
|
1063
1122
|
callbackUrl: flowMeta.callbackUrl,
|
|
1064
1123
|
maxToolCalls: flowMeta.maxToolCalls,
|
|
1124
|
+
tags: flowMeta.tags,
|
|
1065
1125
|
suiteId: null,
|
|
1066
1126
|
nextRunMode: 'DETERMINISTIC',
|
|
1067
1127
|
provenance: (0, buildProvenance_1.buildProvenance)('CODE'),
|
|
@@ -53,6 +53,7 @@ const ToolRequiresGptException_1 = require("../exceptions/ToolRequiresGptExcepti
|
|
|
53
53
|
const UnknownToolException_1 = require("../exceptions/UnknownToolException");
|
|
54
54
|
const CreateDonobuFlow_1 = require("../models/CreateDonobuFlow");
|
|
55
55
|
const GptConfig_1 = require("../models/GptConfig");
|
|
56
|
+
const MetadataVersion_1 = require("../models/MetadataVersion");
|
|
56
57
|
const resolveTargetRuntime_1 = require("../targets/resolveTargetRuntime");
|
|
57
58
|
const CustomToolRunnerTool_1 = require("../tools/CustomToolRunnerTool");
|
|
58
59
|
const buildProvenance_1 = require("../utils/buildProvenance");
|
|
@@ -162,7 +163,7 @@ class DonobuFlowsManager {
|
|
|
162
163
|
const flowMetadata = {
|
|
163
164
|
id: flowId,
|
|
164
165
|
target: flowParams.target,
|
|
165
|
-
metadataVersion:
|
|
166
|
+
metadataVersion: MetadataVersion_1.CURRENT_METADATA_VERSION,
|
|
166
167
|
createdWithDonobuVersion: MiscUtils_1.MiscUtils.DONOBU_VERSION,
|
|
167
168
|
name: flowParams.name || null,
|
|
168
169
|
envVars: allowedEnvVarsByName,
|
|
@@ -185,6 +186,7 @@ class DonobuFlowsManager {
|
|
|
185
186
|
state: 'UNSTARTED',
|
|
186
187
|
nextState: null,
|
|
187
188
|
videoDisabled: flowParams.videoDisabled,
|
|
189
|
+
tags: [],
|
|
188
190
|
testId: flowParams.testId ?? null,
|
|
189
191
|
// Target-specific fields (browser, targetWebsite, isControlPanelEnabled, etc.)
|
|
190
192
|
...targetRuntime.getMetadataFields(),
|
|
@@ -5,6 +5,7 @@ const crypto_1 = require("crypto");
|
|
|
5
5
|
const CannotDeleteRunningFlowException_1 = require("../exceptions/CannotDeleteRunningFlowException");
|
|
6
6
|
const SuiteNotFoundException_1 = require("../exceptions/SuiteNotFoundException");
|
|
7
7
|
const TestNotFoundException_1 = require("../exceptions/TestNotFoundException");
|
|
8
|
+
const MetadataVersion_1 = require("../models/MetadataVersion");
|
|
8
9
|
const buildProvenance_1 = require("../utils/buildProvenance");
|
|
9
10
|
const displayName_1 = require("../utils/displayName");
|
|
10
11
|
const FederatedPagination_1 = require("./FederatedPagination");
|
|
@@ -24,7 +25,7 @@ class TestsManager {
|
|
|
24
25
|
: undefined;
|
|
25
26
|
const testMetadata = {
|
|
26
27
|
id: testId,
|
|
27
|
-
metadataVersion:
|
|
28
|
+
metadataVersion: MetadataVersion_1.CURRENT_METADATA_VERSION,
|
|
28
29
|
name: (0, displayName_1.getDisplayName)({ name: params.name ?? null, web }),
|
|
29
30
|
suiteId: params.suiteId ?? null,
|
|
30
31
|
nextRunMode: params.nextRunMode ?? 'AUTONOMOUS',
|
|
@@ -38,6 +39,7 @@ class TestsManager {
|
|
|
38
39
|
resultJsonSchema: params.resultJsonSchema ?? null,
|
|
39
40
|
maxToolCalls: params.maxToolCalls ?? null,
|
|
40
41
|
videoDisabled: params.videoDisabled,
|
|
42
|
+
tags: [],
|
|
41
43
|
provenance: (0, buildProvenance_1.buildProvenance)('DONOBU_STUDIO'),
|
|
42
44
|
};
|
|
43
45
|
// If the test is part of a suite, write it to the same persistence layer
|
|
@@ -24,6 +24,7 @@ export declare const CreateSuiteSchema: z.ZodObject<{
|
|
|
24
24
|
callbackUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
25
25
|
maxToolCalls: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
26
26
|
videoDisabled: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodBoolean>>>;
|
|
27
|
+
tags: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
27
28
|
name: z.ZodString;
|
|
28
29
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
29
30
|
web: z.ZodOptional<z.ZodObject<{
|
|
@@ -23,6 +23,7 @@ export declare const CreateTestSchema: z.ZodObject<{
|
|
|
23
23
|
callbackUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
24
24
|
maxToolCalls: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
25
25
|
videoDisabled: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodBoolean>>>;
|
|
26
|
+
tags: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
26
27
|
target: z.ZodString;
|
|
27
28
|
web: z.ZodOptional<z.ZodObject<{
|
|
28
29
|
browser: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
@@ -140,6 +140,7 @@ export declare const FlowMetadataSchema: z.ZodObject<{
|
|
|
140
140
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
141
141
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
142
142
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
143
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
143
144
|
id: z.ZodString;
|
|
144
145
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
145
146
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Current metadata schema version. Bump this when the metadata JSON structure
|
|
3
|
+
* changes in a way that requires read-time normalization.
|
|
4
|
+
*
|
|
5
|
+
* Shared by Flow and Test metadata — both schemas carry the field as a single
|
|
6
|
+
* monotonic counter so a single bump covers a schema change that touches
|
|
7
|
+
* either or both.
|
|
8
|
+
*
|
|
9
|
+
* History:
|
|
10
|
+
* v1 — first version to carry `metadataVersion`. Replaced top-level
|
|
11
|
+
* `browser` / `targetWebsite` with the `{ target, web }` wrapper.
|
|
12
|
+
*/
|
|
13
|
+
export declare const CURRENT_METADATA_VERSION = 1;
|
|
14
|
+
//# sourceMappingURL=MetadataVersion.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CURRENT_METADATA_VERSION = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Current metadata schema version. Bump this when the metadata JSON structure
|
|
6
|
+
* changes in a way that requires read-time normalization.
|
|
7
|
+
*
|
|
8
|
+
* Shared by Flow and Test metadata — both schemas carry the field as a single
|
|
9
|
+
* monotonic counter so a single bump covers a schema change that touches
|
|
10
|
+
* either or both.
|
|
11
|
+
*
|
|
12
|
+
* History:
|
|
13
|
+
* v1 — first version to carry `metadataVersion`. Replaced top-level
|
|
14
|
+
* `browser` / `targetWebsite` with the `{ target, web }` wrapper.
|
|
15
|
+
*/
|
|
16
|
+
exports.CURRENT_METADATA_VERSION = 1;
|
|
17
|
+
//# sourceMappingURL=MetadataVersion.js.map
|
|
@@ -255,6 +255,7 @@ export declare const RunConfigSchema: z.ZodObject<{
|
|
|
255
255
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
256
256
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
257
257
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
258
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
258
259
|
}, z.core.$loose>;
|
|
259
260
|
export type RunConfig = z.infer<typeof RunConfigSchema>;
|
|
260
261
|
export {};
|
|
@@ -64,5 +64,13 @@ exports.RunConfigSchema = v4_1.z.looseObject({
|
|
|
64
64
|
.optional()
|
|
65
65
|
.nullable()
|
|
66
66
|
.describe('If true, do not record a video of the execution.'),
|
|
67
|
+
tags: v4_1.z
|
|
68
|
+
.array(v4_1.z.string())
|
|
69
|
+
.optional()
|
|
70
|
+
.describe('Tags applied to this run (e.g. "@smoke", "@regression"). For Playwright ' +
|
|
71
|
+
"tests, populated from Playwright's testInfo.tags — which itself includes " +
|
|
72
|
+
'both tags parsed from the test name and tags from the `tag` test details object. ' +
|
|
73
|
+
'An empty array is meaningful: it signals "this run has no tags," which ' +
|
|
74
|
+
'should override any name-parsed tags that consumers might otherwise infer.'),
|
|
67
75
|
});
|
|
68
76
|
//# sourceMappingURL=RunConfig.js.map
|
|
@@ -136,6 +136,7 @@ export declare const SuiteMetadataSchema: z.ZodObject<{
|
|
|
136
136
|
callbackUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
137
137
|
maxToolCalls: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
138
138
|
videoDisabled: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodBoolean>>>;
|
|
139
|
+
tags: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
139
140
|
id: z.ZodString;
|
|
140
141
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
141
142
|
name: z.ZodString;
|
|
@@ -133,6 +133,7 @@ export declare const TestMetadataSchema: z.ZodObject<{
|
|
|
133
133
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
134
134
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
135
135
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
136
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
136
137
|
id: z.ZodString;
|
|
137
138
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
138
139
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -315,6 +316,7 @@ export declare const TestListItemSchema: z.ZodObject<{
|
|
|
315
316
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
316
317
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
317
318
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
319
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
318
320
|
id: z.ZodString;
|
|
319
321
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
320
322
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -473,6 +475,7 @@ export declare const TestListItemSchema: z.ZodObject<{
|
|
|
473
475
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
474
476
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
475
477
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
478
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
476
479
|
id: z.ZodString;
|
|
477
480
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
478
481
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -665,6 +668,7 @@ export declare const TestListItemPaginatedResultSchema: z.ZodObject<{
|
|
|
665
668
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
666
669
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
667
670
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
671
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
668
672
|
id: z.ZodString;
|
|
669
673
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
670
674
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -823,6 +827,7 @@ export declare const TestListItemPaginatedResultSchema: z.ZodObject<{
|
|
|
823
827
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
824
828
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
825
829
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
830
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
826
831
|
id: z.ZodString;
|
|
827
832
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
828
833
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import type { FlowMetadata } from '../models/FlowMetadata';
|
|
2
|
-
/**
|
|
3
|
-
* Current metadata schema version. Bump this when the metadata JSON structure
|
|
4
|
-
* changes in a way that requires read-time normalization.
|
|
5
|
-
*/
|
|
6
|
-
export declare const CURRENT_METADATA_VERSION = 1;
|
|
7
2
|
/**
|
|
8
3
|
* Normalizes flow metadata written by older SDK versions to the current schema.
|
|
9
4
|
*
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CURRENT_METADATA_VERSION = void 0;
|
|
4
3
|
exports.normalizeFlowMetadata = normalizeFlowMetadata;
|
|
5
|
-
|
|
6
|
-
* Current metadata schema version. Bump this when the metadata JSON structure
|
|
7
|
-
* changes in a way that requires read-time normalization.
|
|
8
|
-
*/
|
|
9
|
-
exports.CURRENT_METADATA_VERSION = 1;
|
|
4
|
+
const MetadataVersion_1 = require("../models/MetadataVersion");
|
|
10
5
|
/**
|
|
11
6
|
* Normalizes flow metadata written by older SDK versions to the current schema.
|
|
12
7
|
*
|
|
@@ -16,7 +11,7 @@ exports.CURRENT_METADATA_VERSION = 1;
|
|
|
16
11
|
* introduced in migration v9).
|
|
17
12
|
*/
|
|
18
13
|
function normalizeFlowMetadata(raw) {
|
|
19
|
-
if (raw.metadataVersion ===
|
|
14
|
+
if (raw.metadataVersion === MetadataVersion_1.CURRENT_METADATA_VERSION) {
|
|
20
15
|
return raw;
|
|
21
16
|
}
|
|
22
17
|
// Pre-v5 format: `browser` and `targetWebsite` at the top level, no `target`.
|
|
@@ -15,6 +15,7 @@ const InteractionVisualizer_1 = require("../../managers/InteractionVisualizer");
|
|
|
15
15
|
const ToolManager_1 = require("../../managers/ToolManager");
|
|
16
16
|
const WebTargetInspector_1 = require("../../managers/WebTargetInspector");
|
|
17
17
|
const ControlPanel_1 = require("../../models/ControlPanel");
|
|
18
|
+
const MetadataVersion_1 = require("../../models/MetadataVersion");
|
|
18
19
|
const AnalyzePageTextTool_1 = require("../../tools/AnalyzePageTextTool");
|
|
19
20
|
const AssertTool_1 = require("../../tools/AssertTool");
|
|
20
21
|
const AuditTool_1 = require("../../tools/AuditTool");
|
|
@@ -88,7 +89,7 @@ async function extendPage(page, options) {
|
|
|
88
89
|
const sharedState = {
|
|
89
90
|
donobuFlowMetadata: {
|
|
90
91
|
id: options?.flowId ?? (0, crypto_1.randomUUID)(),
|
|
91
|
-
metadataVersion:
|
|
92
|
+
metadataVersion: MetadataVersion_1.CURRENT_METADATA_VERSION,
|
|
92
93
|
name: null,
|
|
93
94
|
createdWithDonobuVersion: MiscUtils_1.MiscUtils.DONOBU_VERSION,
|
|
94
95
|
target: 'web',
|
|
@@ -104,6 +105,7 @@ async function extendPage(page, options) {
|
|
|
104
105
|
targetWebsite: PLACEHOLDER_FLOW_URL,
|
|
105
106
|
},
|
|
106
107
|
envVars: options?.envVars ?? null,
|
|
108
|
+
tags: [],
|
|
107
109
|
gptConfigName: null,
|
|
108
110
|
hasGptConfigNameOverride: false,
|
|
109
111
|
customTools: null,
|
|
@@ -409,6 +409,7 @@ exports.test = test_1.test.extend({
|
|
|
409
409
|
extendedPage._dnb.donobuFlowMetadata.name = getSanitizedTestName(testInfo);
|
|
410
410
|
extendedPage._dnb.donobuFlowMetadata.testId = testInfo.testId;
|
|
411
411
|
extendedPage._dnb.donobuFlowMetadata.overallObjective = overallObjective;
|
|
412
|
+
extendedPage._dnb.donobuFlowMetadata.tags = testInfo.tags;
|
|
412
413
|
// Register browser console and network listeners so that logs from these
|
|
413
414
|
// sources are captured into the flow's logBuffer. In Studio-launched flows
|
|
414
415
|
// this is done by WebTargetInspector.initialize(), but that method is not
|
|
@@ -421,12 +422,27 @@ exports.test = test_1.test.extend({
|
|
|
421
422
|
const asyncScope = new async_hooks_1.AsyncResource('DonobuTestContext');
|
|
422
423
|
const boundUse = asyncScope.bind(use);
|
|
423
424
|
try {
|
|
425
|
+
const testMetadata = flowMetadataToTestMetadata(testInfo.testId, extendedPage._dnb.donobuFlowMetadata);
|
|
424
426
|
try {
|
|
425
|
-
const testMetadata = flowMetadataToTestMetadata(testInfo.testId, extendedPage._dnb.donobuFlowMetadata);
|
|
426
427
|
await extendedPage._dnb.testsPersistence.createTest(testMetadata);
|
|
427
428
|
}
|
|
428
429
|
catch {
|
|
429
|
-
// Test already exists from a prior run
|
|
430
|
+
// Test already exists from a prior run. Refresh the fields the SDK
|
|
431
|
+
// is the source of truth for so changes in the test code (e.g. a
|
|
432
|
+
// new `@tag`, an updated overallObjective annotation, additional
|
|
433
|
+
// `ENV` annotations, a different target URL) flow through to the
|
|
434
|
+
// persisted record — while leaving Studio-owned fields like
|
|
435
|
+
// `suiteId` and `nextRunMode` untouched.
|
|
436
|
+
const existing = await extendedPage._dnb.testsPersistence.getTestById(testInfo.testId);
|
|
437
|
+
await extendedPage._dnb.testsPersistence.updateTest({
|
|
438
|
+
...existing,
|
|
439
|
+
provenance: testMetadata.provenance,
|
|
440
|
+
web: testMetadata.web,
|
|
441
|
+
envVars: testMetadata.envVars,
|
|
442
|
+
overallObjective: testMetadata.overallObjective,
|
|
443
|
+
allowedTools: testMetadata.allowedTools,
|
|
444
|
+
tags: testMetadata.tags,
|
|
445
|
+
});
|
|
430
446
|
}
|
|
431
447
|
await extendedPage._dnb.persistence.setFlowMetadata(extendedPage._dnb.donobuFlowMetadata);
|
|
432
448
|
await boundUse(extendedPage);
|
|
@@ -846,6 +862,43 @@ async function attachStepScreenshots(sharedState, testInfo) {
|
|
|
846
862
|
contentType: 'application/json',
|
|
847
863
|
});
|
|
848
864
|
}
|
|
865
|
+
/**
|
|
866
|
+
* Build the per-flow `donobu-test-result.json` payload the website renders as
|
|
867
|
+
* the report's summary row + error/skip sections: the test-level scalars
|
|
868
|
+
* (status, objective, annotations, assertion errors).
|
|
869
|
+
*/
|
|
870
|
+
function buildDonobuTestResult(testInfo) {
|
|
871
|
+
const objective = testInfo.annotations.find((a) => a.type === 'objective')?.description ??
|
|
872
|
+
null;
|
|
873
|
+
return {
|
|
874
|
+
status: testInfo.status ?? 'unknown',
|
|
875
|
+
specTitle: testInfo.title,
|
|
876
|
+
file: path_1.default.relative(testInfo.config.rootDir, testInfo.file),
|
|
877
|
+
projectName: testInfo.project.name,
|
|
878
|
+
durationMs: testInfo.duration,
|
|
879
|
+
objective,
|
|
880
|
+
annotations: testInfo.annotations,
|
|
881
|
+
// TestInfoError exposes only message/stack at runtime; the assertion diff
|
|
882
|
+
// (expected/received) is embedded in the message for expect() failures, and
|
|
883
|
+
// the stack carries the code frame — enough for the website's error block.
|
|
884
|
+
errors: (testInfo.errors ?? []).map((e) => ({
|
|
885
|
+
message: e.message,
|
|
886
|
+
stack: e.stack,
|
|
887
|
+
})),
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Upload a per-flow JSON artifact through the flow's persistence layer.
|
|
892
|
+
* Best-effort: a failed upload must never affect the test result.
|
|
893
|
+
*/
|
|
894
|
+
async function persistFlowJson(persistence, flowId, fileId, value) {
|
|
895
|
+
try {
|
|
896
|
+
await persistence.setFlowFile(flowId, fileId, Buffer.from(JSON.stringify(value), 'utf8'));
|
|
897
|
+
}
|
|
898
|
+
catch (error) {
|
|
899
|
+
Logger_1.appLogger.warn(`Failed to persist report artifact "${fileId}" for flow ${flowId}: ${error.message}`);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
849
902
|
/**
|
|
850
903
|
* Capture a live screenshot of the flow's final visual state at teardown (page
|
|
851
904
|
* still open) and persist it as a per-flow file — the single source of truth
|
|
@@ -855,14 +908,14 @@ async function attachStepScreenshots(sharedState, testInfo) {
|
|
|
855
908
|
* See `fetchBaselineScreenshot` / `gatherTestFailureEvidence` in
|
|
856
909
|
* triageTestFailure.ts.
|
|
857
910
|
*
|
|
858
|
-
* Runs for any meaningful end state
|
|
859
|
-
*
|
|
860
|
-
*
|
|
911
|
+
* Runs for any meaningful end state, including V1 (objective-annotated) tests;
|
|
912
|
+
* skipped only for `skipped` tests (no real page state) or when triage is
|
|
913
|
+
* disabled. Triage reads this screenshot as the current run's failure shot and
|
|
914
|
+
* as the baseline for a later failing run. Best-effort and fails open.
|
|
861
915
|
*/
|
|
862
916
|
async function captureAndPersistFinalState(page, testInfo) {
|
|
863
917
|
if (testInfo.status === 'skipped' ||
|
|
864
|
-
process.env.DONOBU_TRIAGE_DISABLED === '1'
|
|
865
|
-
isV1Test(testInfo)) {
|
|
918
|
+
process.env.DONOBU_TRIAGE_DISABLED === '1') {
|
|
866
919
|
return;
|
|
867
920
|
}
|
|
868
921
|
const flowId = page._dnb?.donobuFlowMetadata?.id;
|
|
@@ -902,16 +955,15 @@ async function finalizeTest(page, testInfo, logBuffer, videoOption) {
|
|
|
902
955
|
body: JSON.stringify(sharedState.donobuFlowMetadata, null, 2),
|
|
903
956
|
contentType: 'application/json',
|
|
904
957
|
});
|
|
958
|
+
// Persist the per-flow test result (status, objective, annotations, and
|
|
959
|
+
// assertion errors with snippets/diffs) so the website can render the
|
|
960
|
+
// report's summary row + error section. Playwright's TestInfoError already
|
|
961
|
+
// carries the code `snippet`, so no source-file access is needed.
|
|
962
|
+
await persistFlowJson(sharedState.persistence, sharedState.donobuFlowMetadata.id, 'donobu-test-result.json', buildDonobuTestResult(testInfo));
|
|
905
963
|
// Persist captured flow logs so they are available in the Donobu UI,
|
|
906
964
|
// mirroring what DonobuFlowsManager does for Studio-launched flows.
|
|
907
965
|
if (logBuffer) {
|
|
908
|
-
|
|
909
|
-
const snapshot = logBuffer.snapshot();
|
|
910
|
-
await sharedState.persistence.setFlowFile(sharedState.donobuFlowMetadata.id, 'logs.json', Buffer.from(JSON.stringify(snapshot)));
|
|
911
|
-
}
|
|
912
|
-
catch (error) {
|
|
913
|
-
Logger_1.appLogger.error('Failed to persist flow logs:', error);
|
|
914
|
-
}
|
|
966
|
+
await persistFlowJson(sharedState.persistence, sharedState.donobuFlowMetadata.id, 'logs.json', logBuffer.snapshot());
|
|
915
967
|
}
|
|
916
968
|
// Attach step-level screenshots from the flow's tool call history.
|
|
917
969
|
// These enable the HTML report to show a visual timeline of what the
|
|
@@ -927,6 +979,7 @@ async function finalizeTest(page, testInfo, logBuffer, videoOption) {
|
|
|
927
979
|
body: JSON.stringify(nativeSteps),
|
|
928
980
|
contentType: 'application/json',
|
|
929
981
|
});
|
|
982
|
+
await persistFlowJson(sharedState.persistence, sharedState.donobuFlowMetadata.id, 'donobu-native-steps.json', nativeSteps);
|
|
930
983
|
}
|
|
931
984
|
}
|
|
932
985
|
catch {
|
|
@@ -945,6 +998,7 @@ async function finalizeTest(page, testInfo, logBuffer, videoOption) {
|
|
|
945
998
|
body: JSON.stringify(aiInvocations),
|
|
946
999
|
contentType: 'application/json',
|
|
947
1000
|
});
|
|
1001
|
+
await persistFlowJson(sharedState.persistence, sharedState.donobuFlowMetadata.id, 'donobu-ai-invocations.json', aiInvocations);
|
|
948
1002
|
}
|
|
949
1003
|
}
|
|
950
1004
|
catch {
|
|
@@ -961,35 +1015,40 @@ async function finalizeTest(page, testInfo, logBuffer, videoOption) {
|
|
|
961
1015
|
// future failing run reads a successful run's copy as its baseline.
|
|
962
1016
|
await captureAndPersistFinalState(page, testInfo);
|
|
963
1017
|
if (testInfo.status === 'failed') {
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
}
|
|
977
|
-
}
|
|
1018
|
+
// Gather failure-triage evidence for every failed test, regardless of its
|
|
1019
|
+
// V1 (objective-annotated) classification or self-heal setting. Triage is a
|
|
1020
|
+
// standalone diagnostic: it writes the failure evidence that populates the
|
|
1021
|
+
// triage run directory, feeds the reports, and supplies the treatment plans
|
|
1022
|
+
// auto-heal consumes. Legacy V1 self-heal runs separately, below.
|
|
1023
|
+
try {
|
|
1024
|
+
const evidenceResult = await (0, triageTestFailure_1.gatherTestFailureEvidence)(testInfo, page);
|
|
1025
|
+
if (evidenceResult?.filePath) {
|
|
1026
|
+
Logger_1.appLogger.info(`Persisted Donobu triage evidence for "${testInfo.title}" to ${evidenceResult.filePath}.`);
|
|
1027
|
+
}
|
|
1028
|
+
else if (evidenceResult?.evidence) {
|
|
1029
|
+
Logger_1.appLogger.info(`Captured Donobu triage evidence for "${testInfo.title}" (schema v${evidenceResult.evidence.schemaVersion}).`);
|
|
978
1030
|
}
|
|
979
1031
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1032
|
+
catch (error) {
|
|
1033
|
+
Logger_1.appLogger.error(`Failed to gather test failure evidence for "${testInfo.title}".`, error);
|
|
1034
|
+
}
|
|
1035
|
+
// Legacy V1 self-heal: only for objective-annotated tests that opt in via
|
|
1036
|
+
// SELF_HEAL_TESTS_ENABLED, and never during an auto-heal rerun (which owns
|
|
1037
|
+
// its own remediation path).
|
|
1038
|
+
if (isV1Test(testInfo) &&
|
|
1039
|
+
isV1SelfHealingEnabled(testInfo) &&
|
|
1040
|
+
!MiscUtils_1.MiscUtils.yn(envVars_1.env.data.DONOBU_AUTO_HEAL_ACTIVE)) {
|
|
1041
|
+
if (!sharedState.gptClient) {
|
|
1042
|
+
Logger_1.appLogger.warn('Will not self-heal due to no GPT client being set up.');
|
|
1043
|
+
}
|
|
1044
|
+
else {
|
|
1045
|
+
try {
|
|
1046
|
+
await (0, selfHealing_1.selfHeal)(sharedState.gptClient, testInfo, page);
|
|
985
1047
|
}
|
|
986
|
-
|
|
987
|
-
Logger_1.appLogger.
|
|
1048
|
+
catch (error) {
|
|
1049
|
+
Logger_1.appLogger.error('Error when attempting to self-heal', error);
|
|
988
1050
|
}
|
|
989
1051
|
}
|
|
990
|
-
catch (error) {
|
|
991
|
-
Logger_1.appLogger.error(`Failed to gather test failure evidence for "${testInfo.title}".`, error);
|
|
992
|
-
}
|
|
993
1052
|
}
|
|
994
1053
|
}
|
|
995
1054
|
else if (testInfo.status === 'passed' &&
|
|
@@ -1062,6 +1121,7 @@ function flowMetadataToTestMetadata(testId, flowMeta) {
|
|
|
1062
1121
|
resultJsonSchema: flowMeta.resultJsonSchema,
|
|
1063
1122
|
callbackUrl: flowMeta.callbackUrl,
|
|
1064
1123
|
maxToolCalls: flowMeta.maxToolCalls,
|
|
1124
|
+
tags: flowMeta.tags,
|
|
1065
1125
|
suiteId: null,
|
|
1066
1126
|
nextRunMode: 'DETERMINISTIC',
|
|
1067
1127
|
provenance: (0, buildProvenance_1.buildProvenance)('CODE'),
|
|
@@ -53,6 +53,7 @@ const ToolRequiresGptException_1 = require("../exceptions/ToolRequiresGptExcepti
|
|
|
53
53
|
const UnknownToolException_1 = require("../exceptions/UnknownToolException");
|
|
54
54
|
const CreateDonobuFlow_1 = require("../models/CreateDonobuFlow");
|
|
55
55
|
const GptConfig_1 = require("../models/GptConfig");
|
|
56
|
+
const MetadataVersion_1 = require("../models/MetadataVersion");
|
|
56
57
|
const resolveTargetRuntime_1 = require("../targets/resolveTargetRuntime");
|
|
57
58
|
const CustomToolRunnerTool_1 = require("../tools/CustomToolRunnerTool");
|
|
58
59
|
const buildProvenance_1 = require("../utils/buildProvenance");
|
|
@@ -162,7 +163,7 @@ class DonobuFlowsManager {
|
|
|
162
163
|
const flowMetadata = {
|
|
163
164
|
id: flowId,
|
|
164
165
|
target: flowParams.target,
|
|
165
|
-
metadataVersion:
|
|
166
|
+
metadataVersion: MetadataVersion_1.CURRENT_METADATA_VERSION,
|
|
166
167
|
createdWithDonobuVersion: MiscUtils_1.MiscUtils.DONOBU_VERSION,
|
|
167
168
|
name: flowParams.name || null,
|
|
168
169
|
envVars: allowedEnvVarsByName,
|
|
@@ -185,6 +186,7 @@ class DonobuFlowsManager {
|
|
|
185
186
|
state: 'UNSTARTED',
|
|
186
187
|
nextState: null,
|
|
187
188
|
videoDisabled: flowParams.videoDisabled,
|
|
189
|
+
tags: [],
|
|
188
190
|
testId: flowParams.testId ?? null,
|
|
189
191
|
// Target-specific fields (browser, targetWebsite, isControlPanelEnabled, etc.)
|
|
190
192
|
...targetRuntime.getMetadataFields(),
|
|
@@ -5,6 +5,7 @@ const crypto_1 = require("crypto");
|
|
|
5
5
|
const CannotDeleteRunningFlowException_1 = require("../exceptions/CannotDeleteRunningFlowException");
|
|
6
6
|
const SuiteNotFoundException_1 = require("../exceptions/SuiteNotFoundException");
|
|
7
7
|
const TestNotFoundException_1 = require("../exceptions/TestNotFoundException");
|
|
8
|
+
const MetadataVersion_1 = require("../models/MetadataVersion");
|
|
8
9
|
const buildProvenance_1 = require("../utils/buildProvenance");
|
|
9
10
|
const displayName_1 = require("../utils/displayName");
|
|
10
11
|
const FederatedPagination_1 = require("./FederatedPagination");
|
|
@@ -24,7 +25,7 @@ class TestsManager {
|
|
|
24
25
|
: undefined;
|
|
25
26
|
const testMetadata = {
|
|
26
27
|
id: testId,
|
|
27
|
-
metadataVersion:
|
|
28
|
+
metadataVersion: MetadataVersion_1.CURRENT_METADATA_VERSION,
|
|
28
29
|
name: (0, displayName_1.getDisplayName)({ name: params.name ?? null, web }),
|
|
29
30
|
suiteId: params.suiteId ?? null,
|
|
30
31
|
nextRunMode: params.nextRunMode ?? 'AUTONOMOUS',
|
|
@@ -38,6 +39,7 @@ class TestsManager {
|
|
|
38
39
|
resultJsonSchema: params.resultJsonSchema ?? null,
|
|
39
40
|
maxToolCalls: params.maxToolCalls ?? null,
|
|
40
41
|
videoDisabled: params.videoDisabled,
|
|
42
|
+
tags: [],
|
|
41
43
|
provenance: (0, buildProvenance_1.buildProvenance)('DONOBU_STUDIO'),
|
|
42
44
|
};
|
|
43
45
|
// If the test is part of a suite, write it to the same persistence layer
|
|
@@ -24,6 +24,7 @@ export declare const CreateSuiteSchema: z.ZodObject<{
|
|
|
24
24
|
callbackUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
25
25
|
maxToolCalls: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
26
26
|
videoDisabled: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodBoolean>>>;
|
|
27
|
+
tags: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
27
28
|
name: z.ZodString;
|
|
28
29
|
description: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
29
30
|
web: z.ZodOptional<z.ZodObject<{
|
|
@@ -23,6 +23,7 @@ export declare const CreateTestSchema: z.ZodObject<{
|
|
|
23
23
|
callbackUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
24
24
|
maxToolCalls: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
25
25
|
videoDisabled: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodBoolean>>>;
|
|
26
|
+
tags: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
26
27
|
target: z.ZodString;
|
|
27
28
|
web: z.ZodOptional<z.ZodObject<{
|
|
28
29
|
browser: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
@@ -140,6 +140,7 @@ export declare const FlowMetadataSchema: z.ZodObject<{
|
|
|
140
140
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
141
141
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
142
142
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
143
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
143
144
|
id: z.ZodString;
|
|
144
145
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
145
146
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Current metadata schema version. Bump this when the metadata JSON structure
|
|
3
|
+
* changes in a way that requires read-time normalization.
|
|
4
|
+
*
|
|
5
|
+
* Shared by Flow and Test metadata — both schemas carry the field as a single
|
|
6
|
+
* monotonic counter so a single bump covers a schema change that touches
|
|
7
|
+
* either or both.
|
|
8
|
+
*
|
|
9
|
+
* History:
|
|
10
|
+
* v1 — first version to carry `metadataVersion`. Replaced top-level
|
|
11
|
+
* `browser` / `targetWebsite` with the `{ target, web }` wrapper.
|
|
12
|
+
*/
|
|
13
|
+
export declare const CURRENT_METADATA_VERSION = 1;
|
|
14
|
+
//# sourceMappingURL=MetadataVersion.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CURRENT_METADATA_VERSION = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Current metadata schema version. Bump this when the metadata JSON structure
|
|
6
|
+
* changes in a way that requires read-time normalization.
|
|
7
|
+
*
|
|
8
|
+
* Shared by Flow and Test metadata — both schemas carry the field as a single
|
|
9
|
+
* monotonic counter so a single bump covers a schema change that touches
|
|
10
|
+
* either or both.
|
|
11
|
+
*
|
|
12
|
+
* History:
|
|
13
|
+
* v1 — first version to carry `metadataVersion`. Replaced top-level
|
|
14
|
+
* `browser` / `targetWebsite` with the `{ target, web }` wrapper.
|
|
15
|
+
*/
|
|
16
|
+
exports.CURRENT_METADATA_VERSION = 1;
|
|
17
|
+
//# sourceMappingURL=MetadataVersion.js.map
|
|
@@ -255,6 +255,7 @@ export declare const RunConfigSchema: z.ZodObject<{
|
|
|
255
255
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
256
256
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
257
257
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
258
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
258
259
|
}, z.core.$loose>;
|
|
259
260
|
export type RunConfig = z.infer<typeof RunConfigSchema>;
|
|
260
261
|
export {};
|
package/dist/models/RunConfig.js
CHANGED
|
@@ -64,5 +64,13 @@ exports.RunConfigSchema = v4_1.z.looseObject({
|
|
|
64
64
|
.optional()
|
|
65
65
|
.nullable()
|
|
66
66
|
.describe('If true, do not record a video of the execution.'),
|
|
67
|
+
tags: v4_1.z
|
|
68
|
+
.array(v4_1.z.string())
|
|
69
|
+
.optional()
|
|
70
|
+
.describe('Tags applied to this run (e.g. "@smoke", "@regression"). For Playwright ' +
|
|
71
|
+
"tests, populated from Playwright's testInfo.tags — which itself includes " +
|
|
72
|
+
'both tags parsed from the test name and tags from the `tag` test details object. ' +
|
|
73
|
+
'An empty array is meaningful: it signals "this run has no tags," which ' +
|
|
74
|
+
'should override any name-parsed tags that consumers might otherwise infer.'),
|
|
67
75
|
});
|
|
68
76
|
//# sourceMappingURL=RunConfig.js.map
|
|
@@ -136,6 +136,7 @@ export declare const SuiteMetadataSchema: z.ZodObject<{
|
|
|
136
136
|
callbackUrl: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
137
137
|
maxToolCalls: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
138
138
|
videoDisabled: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodBoolean>>>;
|
|
139
|
+
tags: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
139
140
|
id: z.ZodString;
|
|
140
141
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
141
142
|
name: z.ZodString;
|
|
@@ -133,6 +133,7 @@ export declare const TestMetadataSchema: z.ZodObject<{
|
|
|
133
133
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
134
134
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
135
135
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
136
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
136
137
|
id: z.ZodString;
|
|
137
138
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
138
139
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -315,6 +316,7 @@ export declare const TestListItemSchema: z.ZodObject<{
|
|
|
315
316
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
316
317
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
317
318
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
319
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
318
320
|
id: z.ZodString;
|
|
319
321
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
320
322
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -473,6 +475,7 @@ export declare const TestListItemSchema: z.ZodObject<{
|
|
|
473
475
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
474
476
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
475
477
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
478
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
476
479
|
id: z.ZodString;
|
|
477
480
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
478
481
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -665,6 +668,7 @@ export declare const TestListItemPaginatedResultSchema: z.ZodObject<{
|
|
|
665
668
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
666
669
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
667
670
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
671
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
668
672
|
id: z.ZodString;
|
|
669
673
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
670
674
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -823,6 +827,7 @@ export declare const TestListItemPaginatedResultSchema: z.ZodObject<{
|
|
|
823
827
|
callbackUrl: z.ZodNullable<z.ZodString>;
|
|
824
828
|
maxToolCalls: z.ZodNullable<z.ZodNumber>;
|
|
825
829
|
videoDisabled: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>;
|
|
830
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
826
831
|
id: z.ZodString;
|
|
827
832
|
metadataVersion: z.ZodOptional<z.ZodNumber>;
|
|
828
833
|
name: z.ZodNullable<z.ZodString>;
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import type { FlowMetadata } from '../models/FlowMetadata';
|
|
2
|
-
/**
|
|
3
|
-
* Current metadata schema version. Bump this when the metadata JSON structure
|
|
4
|
-
* changes in a way that requires read-time normalization.
|
|
5
|
-
*/
|
|
6
|
-
export declare const CURRENT_METADATA_VERSION = 1;
|
|
7
2
|
/**
|
|
8
3
|
* Normalizes flow metadata written by older SDK versions to the current schema.
|
|
9
4
|
*
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CURRENT_METADATA_VERSION = void 0;
|
|
4
3
|
exports.normalizeFlowMetadata = normalizeFlowMetadata;
|
|
5
|
-
|
|
6
|
-
* Current metadata schema version. Bump this when the metadata JSON structure
|
|
7
|
-
* changes in a way that requires read-time normalization.
|
|
8
|
-
*/
|
|
9
|
-
exports.CURRENT_METADATA_VERSION = 1;
|
|
4
|
+
const MetadataVersion_1 = require("../models/MetadataVersion");
|
|
10
5
|
/**
|
|
11
6
|
* Normalizes flow metadata written by older SDK versions to the current schema.
|
|
12
7
|
*
|
|
@@ -16,7 +11,7 @@ exports.CURRENT_METADATA_VERSION = 1;
|
|
|
16
11
|
* introduced in migration v9).
|
|
17
12
|
*/
|
|
18
13
|
function normalizeFlowMetadata(raw) {
|
|
19
|
-
if (raw.metadataVersion ===
|
|
14
|
+
if (raw.metadataVersion === MetadataVersion_1.CURRENT_METADATA_VERSION) {
|
|
20
15
|
return raw;
|
|
21
16
|
}
|
|
22
17
|
// Pre-v5 format: `browser` and `targetWebsite` at the top level, no `target`.
|