From 16853a19b77e8ddc0d0f33b2fd0818283ac8747d Mon Sep 17 00:00:00 2001 From: Ihar Tsykala Date: Tue, 5 May 2026 19:03:24 +0500 Subject: [PATCH 01/10] feat: 5910 form submit test checkpoint input capture --- .../policy-test-automation-draft.service.ts | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts new file mode 100644 index 0000000000..4823c6ad82 --- /dev/null +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts @@ -0,0 +1,147 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; + +export interface PolicyTestInputAnchor { + policyId: string; + blockId: string; + blockType?: string; + title?: string; + ref?: unknown; + document: unknown; + draft?: boolean; + draftId?: string | null; + relayerAccount?: unknown; + evidence?: Array<{ dataType: 'message' | 'file'; data: string }>; + capturedAt: string; +} + +export interface PolicyTestOutputAnchor { + type: string; + id: string; + document?: unknown; + capturedAt: string; +} + +export interface PolicyTestAutomationDraft { + captureNextFormSubmit: boolean; + input: PolicyTestInputAnchor | null; + outputs: PolicyTestOutputAnchor[]; + name: string; + description: string; + readyToSave: boolean; +} + +const createInitialDraft = (): PolicyTestAutomationDraft => ({ + captureNextFormSubmit: false, + input: null, + outputs: [], + name: '', + description: '', + readyToSave: false +}); + +@Injectable({ providedIn: 'root' }) +export class PolicyTestAutomationDraftService { + private readonly draftSubject = new BehaviorSubject(createInitialDraft()); + public readonly draft$ = this.draftSubject.asObservable(); + + public get draft(): PolicyTestAutomationDraft { + return this.draftSubject.value; + } + + public setCaptureNextFormSubmit(value: boolean): void { + const draft = this.draft; + if (value && draft.input) { + return; + } + this.update({ captureNextFormSubmit: value }); + } + + public captureInput(input: Omit): void { + if (this.draft.input) { + return; + } + this.update({ + captureNextFormSubmit: false, + input: { + ...this.clone(input), + capturedAt: new Date().toISOString() + } + }); + } + + public discardInput(): void { + this.update({ input: null, captureNextFormSubmit: false }); + } + + public addOutput(output: Omit): void { + const exists = this.draft.outputs.some((item) => { + return item.type === output.type && item.id === output.id; + }); + if (exists) { + return; + } + this.update({ + outputs: [ + ...this.draft.outputs, + { ...this.clone(output), capturedAt: new Date().toISOString() } + ] + }); + } + + public discardOutput(type: string, id: string): void { + this.update({ + outputs: this.draft.outputs.filter((item) => item.type !== type || item.id !== id) + }); + } + + public setMetadata(name: string, description: string): void { + this.update({ name, description }); + } + + public markReadyToSave(): boolean { + const readyToSave = !!this.draft.input && this.draft.outputs.length > 0; + this.update({ readyToSave }); + return readyToSave; + } + + public hasInput(): boolean { + return !!this.draft.input; + } + + public hasOutputs(): boolean { + return this.draft.outputs.length > 0; + } + + public shouldWarnBeforeStop(): boolean { + return this.hasInput() && !this.hasOutputs(); + } + + public getRecordMetadata(): { version: 1; name?: string; description?: string; input?: PolicyTestInputAnchor; outputs?: PolicyTestOutputAnchor[] } | null { + if (!this.draft.input || !this.draft.outputs.length) { + return null; + } + return { + version: 1, + name: this.draft.name || undefined, + description: this.draft.description || undefined, + input: this.draft.input, + outputs: this.draft.outputs + }; + } + + public reset(): void { + this.draftSubject.next(createInitialDraft()); + } + + private update(patch: Partial): void { + this.draftSubject.next({ + ...this.draft, + ...patch + }); + } + + private clone(value: T): T { + return JSON.parse(JSON.stringify(value)); + } +} From 328aad69d55756e4cdda94618e3432383ffa9464 Mon Sep 17 00:00:00 2001 From: Ihar Tsykala Date: Tue, 5 May 2026 19:37:18 +0500 Subject: [PATCH 02/10] feat: 5910 Add Policy Test Automation popup with armed state --- .../policy-engine/policy-engine.module.ts | 4 +- ...olicy-test-automation-popup.component.html | 26 +++++++++++++ ...olicy-test-automation-popup.component.scss | 38 +++++++++++++++++++ .../policy-test-automation-popup.component.ts | 19 ++++++++++ .../policy-viewer.component.html | 12 ++++++ 5 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html create mode 100644 frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss create mode 100644 frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts diff --git a/frontend/src/app/modules/policy-engine/policy-engine.module.ts b/frontend/src/app/modules/policy-engine/policy-engine.module.ts index e946655e85..1292bf6a50 100644 --- a/frontend/src/app/modules/policy-engine/policy-engine.module.ts +++ b/frontend/src/app/modules/policy-engine/policy-engine.module.ts @@ -181,6 +181,7 @@ import { ChangeBlockSettingsDialog } from './dialogs/change-block-settings-dialo import { ApproveUpdateVcDocumentDialogComponent } from './dialogs/approve-update-vc-document-dialog/approve-update-vc-document-dialog.component' import { AddDocumentDialog } from './dialogs/add-document-dialog/add-document-dialog.component'; import { MockDialog } from './dialogs/mock-dialog/mock-dialog.component'; +import { PolicyTestAutomationPopupComponent } from './policy-viewer/policy-test-automation/policy-test-automation-popup.component'; @NgModule({ declarations: [ @@ -318,7 +319,8 @@ import { MockDialog } from './dialogs/mock-dialog/mock-dialog.component'; AddDocumentDialog, ChangeBlockSettingsDialog, ApproveUpdateVcDocumentDialogComponent, - MockDialog + MockDialog, + PolicyTestAutomationPopupComponent ], imports: [ CommonModule, diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html new file mode 100644 index 0000000000..8abc98c952 --- /dev/null +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html @@ -0,0 +1,26 @@ +
+
Policy Test Automation
+ +
+
Input
+ + + +
+
Form input captured
+ +
+
+ +
+
Outputs
+
Confirm Document Results
+
+
diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss new file mode 100644 index 0000000000..b949343069 --- /dev/null +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss @@ -0,0 +1,38 @@ +.policy-test-automation-popup { + width: 280px; + padding: 12px; + color: #23252e; + font-size: 12px; +} + +.policy-test-automation-title { + font-weight: 600; + margin-bottom: 12px; +} + +.policy-test-section { + margin-bottom: 12px; +} + +.policy-test-section-title { + font-weight: 600; + margin-bottom: 6px; +} + +.policy-test-checkbox-row { + display: flex; + align-items: center; + gap: 8px; +} + +.policy-test-section-disabled { + opacity: 0.45; +} + +.policy-test-link-button { + border: 0; + background: none; + color: var(--color-primary, #3f51b5); + cursor: pointer; + padding: 0; +} diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts new file mode 100644 index 0000000000..2cf60aca63 --- /dev/null +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; +import { PolicyTestAutomationDraftService } from './policy-test-automation-draft.service'; + +@Component({ + selector: 'policy-test-automation-popup', + templateUrl: './policy-test-automation-popup.component.html', + styleUrls: ['./policy-test-automation-popup.component.scss'] +}) +export class PolicyTestAutomationPopupComponent { + constructor(public draftService: PolicyTestAutomationDraftService) {} + + public onCaptureChange(checked: boolean): void { + this.draftService.setCaptureNextFormSubmit(checked); + } + + public discardInput(): void { + this.draftService.discardInput(); + } +} diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-viewer/policy-viewer.component.html b/frontend/src/app/modules/policy-engine/policy-viewer/policy-viewer/policy-viewer.component.html index 0d25630a57..1faead86fd 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-viewer/policy-viewer.component.html +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-viewer/policy-viewer.component.html @@ -184,6 +184,14 @@ Run +
+ + Test +
+ +
+
+
+ + + +
From 6b400895dd4e251f4738613607221b2129689359 Mon Sep 17 00:00:00 2001 From: Ihar Tsykala Date: Tue, 5 May 2026 20:19:10 +0500 Subject: [PATCH 03/10] feat: 5910 Capture form submits in policy test automation --- .../request-document-block-addon.component.ts | 23 +++++++++++---- ...request-document-block-dialog.component.ts | 29 ++++++++++++++----- .../request-document-block.component.ts | 29 ++++++++++++++----- ...olicy-test-automation-popup.component.html | 3 +- 4 files changed, 62 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block-addon/request-document-block-addon.component.ts b/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block-addon/request-document-block-addon.component.ts index 555b2d0afa..2e58f0c21e 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block-addon/request-document-block-addon.component.ts +++ b/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block-addon/request-document-block-addon.component.ts @@ -19,6 +19,7 @@ import { RequestDocumentBlockDialog } from '../request-document-block/dialog/req import { SchemaRulesService } from 'src/app/services/schema-rules.service'; import { prepareVcData } from 'src/app/modules/common/models/prepare-vc-data'; import { PolicyStatus } from '@guardian/interfaces'; +import { PolicyTestAutomationDraftService } from '../../policy-test-automation/policy-test-automation-draft.service'; interface IRequestDocumentAddonData { readonly: boolean; @@ -92,7 +93,8 @@ export class RequestDocumentBlockAddonComponent private fb: UntypedFormBuilder, private dialogService: DialogService, private router: Router, - private changeDetectorRef: ChangeDetectorRef + private changeDetectorRef: ChangeDetectorRef, + private policyTestDraft: PolicyTestAutomationDraftService ) { super(policyEngineService, profile, wsService); this.dataForm = this.fb.group({}); @@ -203,14 +205,25 @@ export class RequestDocumentBlockAddonComponent if (this.dataForm.valid) { const data = this.dataForm.getRawValue(); prepareVcData(data); + const payload = { + document: data, + ref: this.ref?.id, + }; + + if (this.dryRun && this.policyTestDraft.draft.captureNextFormSubmit) { + this.policyTestDraft.captureInput({ + policyId: this.policyId, + blockId: this.id, + blockType: 'requestDocumentBlockAddon', + ...payload + }); + } + this.dialogRef.close(); this.dialogRef = null; this.loading = true; this.policyEngineService - .setBlockData(this.id, this.policyId, { - document: data, - ref: this.ref?.id, - }) + .setBlockData(this.id, this.policyId, payload) .subscribe( // tslint:disable-next-line:no-empty () => { }, diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block/dialog/request-document-block-dialog.component.ts b/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block/dialog/request-document-block-dialog.component.ts index 085f48534a..cbfc0f156f 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block/dialog/request-document-block-dialog.component.ts +++ b/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block/dialog/request-document-block-dialog.component.ts @@ -18,6 +18,7 @@ import { autosaveValueChanged, getMinutesAgoStream } from 'src/app/utils/autosav import { RelayerAccountsService } from 'src/app/services/relayer-accounts.service'; import { AttachedFile } from 'src/app/modules/common/policy-comments/attached-file'; import { IPFSService } from 'src/app/services/ipfs.service'; +import { PolicyTestAutomationDraftService } from '../../../policy-test-automation/policy-test-automation-draft.service'; @Component({ selector: 'request-document-block-dialog', @@ -115,6 +116,7 @@ export class RequestDocumentBlockDialog { private indexedDb: IndexedDbRegistryService, private tablePersist: TablePersistenceService, private ipfsService: IPFSService, + private policyTestDraft: PolicyTestAutomationDraftService, ) { this.parent = this.config.data; this.dataForm = this.fb.group({}); @@ -294,15 +296,26 @@ export class RequestDocumentBlockDialog { const evidence = this.enableAdditionalData ? this.buildEvidence() : undefined; + const payload = { + document: data, + ref: this.docRef, + draft, + draftId, + relayerAccount: this.getRelayerAccount(), + ...(evidence?.length ? { evidence } : {}) + }; + + if (this.dryRun && !draft && this.policyTestDraft.draft.captureNextFormSubmit) { + this.policyTestDraft.captureInput({ + policyId: this.policyId, + blockId: this.id, + blockType: 'requestDocumentBlock', + ...payload + }); + } + this.policyEngineService - .setBlockData(this.id, this.policyId, { - document: data, - ref: this.docRef, - draft: draft, - draftId: draftId, - relayerAccount: this.getRelayerAccount(), - ...(evidence?.length ? { evidence } : {}) - }) + .setBlockData(this.id, this.policyId, payload) .subscribe(() => { setTimeout(() => { this.loading = false; diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block/request-document-block.component.ts b/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block/request-document-block.component.ts index 9d5cc1b32e..779d481aa1 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block/request-document-block.component.ts +++ b/frontend/src/app/modules/policy-engine/policy-viewer/blocks/request-document-block/request-document-block.component.ts @@ -23,6 +23,7 @@ import { PolicyStatus } from '@guardian/interfaces'; import { RelayerAccountsService } from 'src/app/services/relayer-accounts.service'; import { AttachedFile } from 'src/app/modules/common/policy-comments/attached-file'; import { IPFSService } from 'src/app/services/ipfs.service'; +import { PolicyTestAutomationDraftService } from '../../policy-test-automation/policy-test-automation-draft.service'; interface IRequestDocumentData { readonly: boolean; @@ -150,6 +151,7 @@ export class RequestDocumentBlockComponent private indexedDb: IndexedDbRegistryService, private tablePersist: TablePersistenceService, private ipfsService: IPFSService, + private policyTestDraft: PolicyTestAutomationDraftService, ) { super(policyEngineService, profile, wsService); this.dataForm = this.fb.group({}); @@ -419,17 +421,28 @@ export class RequestDocumentBlockComponent const evidence = this.enableAdditionalData ? this.buildEvidence() : undefined; + const payload = { + document: data, + ref: this.ref, + draft, + draftId: this.draftId, + relayerAccount: this.getRelayerAccount(), + ...(evidence?.length ? { evidence } : {}) + }; + + if (this.dryRun && !draft && this.policyTestDraft.draft.captureNextFormSubmit) { + this.policyTestDraft.captureInput({ + policyId: this.policyId, + blockId: this.id, + blockType: 'requestDocumentBlock', + ...payload + }); + } + let requestSucceeded = false; this.policyEngineService - .setBlockData(this.id, this.policyId, { - document: data, - ref: this.ref, - draft, - draftId: this.draftId, - relayerAccount: this.getRelayerAccount(), - ...(evidence?.length ? { evidence } : {}) - }) + .setBlockData(this.id, this.policyId, payload) .pipe( finalize(async () => { try { diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html index 8abc98c952..97ff3b9cf2 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html @@ -14,7 +14,8 @@
-
Form input captured
+
Input captured
+
AWAITING OUTPUTS
From dcaf4f0716bbb4b945697dbad71627086ed50d34 Mon Sep 17 00:00:00 2001 From: Ihar Tsykala Date: Tue, 5 May 2026 22:08:10 +0500 Subject: [PATCH 04/10] fix: 5910 ui popap --- ...olicy-test-automation-popup.component.html | 9 ++++ ...olicy-test-automation-popup.component.scss | 42 +++++++++++++++++++ .../policy-viewer.component.html | 12 ------ .../record-controller.component.html | 13 ++++-- .../record-controller.component.scss | 41 ++++++++++++++++++ 5 files changed, 101 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html index 97ff3b9cf2..c1250d73da 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html @@ -13,6 +13,15 @@ Capture Form Submission Data +
+ AWAITING INPUT +
+ +
+ + Data on next form submission will be used for test input +
+
Input captured
AWAITING OUTPUTS
diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss index b949343069..5cab394a9b 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss @@ -36,3 +36,45 @@ cursor: pointer; padding: 0; } + +.policy-test-captured-input { + display: flex; + flex-direction: column; + gap: 6px; +} + +.policy-test-input-name { + font-weight: 600; +} + +.policy-test-muted { + color: #999; + font-size: 11px; +} + +.policy-test-awaiting { + color: #f39c12; + font-weight: 600; + margin-bottom: 6px; +} + +.policy-test-help { + display: flex; + align-items: flex-start; + gap: 6px; + background: #f0f0f0; + padding: 8px; + border-radius: 4px; + font-size: 11px; + color: #666; + margin-bottom: 6px; + + i { + margin-top: 1px; + flex-shrink: 0; + } + + span { + line-height: 1.4; + } +} diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-viewer/policy-viewer.component.html b/frontend/src/app/modules/policy-engine/policy-viewer/policy-viewer/policy-viewer.component.html index 1faead86fd..0d25630a57 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-viewer/policy-viewer.component.html +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-viewer/policy-viewer.component.html @@ -184,14 +184,6 @@ Run
-
- - Test -
- -
-
-
- - - -
diff --git a/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.html b/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.html index 367aa873ea..6bf7a8cb53 100644 --- a/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.html +++ b/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.html @@ -10,11 +10,12 @@
REC
-
- +
+ TEST +
-
- +
+ STOP
@@ -36,6 +37,10 @@
+ + + + diff --git a/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.scss b/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.scss index 60c40c6f14..20d295fbae 100644 --- a/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.scss +++ b/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.scss @@ -266,4 +266,45 @@ top: 0; z-index: 1; pointer-events: all; +} + +.recording-test-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + font-size: 12px; + font-weight: 500; + color: #646464; + + span { + line-height: 1; + } + + i { + font-size: 10px; + } +} + +.recording-stop-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + font-size: 12px; + font-weight: 500; + color: #f00; + + span { + line-height: 1; + } +} + +::ng-deep .policy-test-record-overlay { + .p-overlayPanel-content { + padding: 0; + border: none; + box-shadow: none; + background: transparent; + } } \ No newline at end of file From 2c5d5f1fde6067a078d2205e2a1de8b23d6e2d34 Mon Sep 17 00:00:00 2001 From: Ihar Tsykala Date: Wed, 6 May 2026 01:47:35 +0500 Subject: [PATCH 05/10] feat: 5911 confirm output data golden record checkpoint --- .../policy-test-automation-draft.service.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts index 4823c6ad82..a6f3155c19 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts @@ -11,14 +11,22 @@ export interface PolicyTestInputAnchor { draft?: boolean; draftId?: string | null; relayerAccount?: unknown; - evidence?: Array<{ dataType: 'message' | 'file'; data: string }>; + evidence?: { dataType: 'message' | 'file'; data: string }[]; capturedAt: string; } export interface PolicyTestOutputAnchor { - type: string; + type: 'vc' | 'vp' | 'schema' | string; id: string; + title?: string; document?: unknown; + source?: { + policyId?: string; + documentId?: string; + schemaId?: string; + messageId?: string; + rowId?: string; + }; capturedAt: string; } @@ -85,13 +93,15 @@ export class PolicyTestAutomationDraftService { outputs: [ ...this.draft.outputs, { ...this.clone(output), capturedAt: new Date().toISOString() } - ] + ], + readyToSave: false }); } public discardOutput(type: string, id: string): void { this.update({ - outputs: this.draft.outputs.filter((item) => item.type !== type || item.id !== id) + outputs: this.draft.outputs.filter((item) => item.type !== type || item.id !== id), + readyToSave: false }); } From f8b9addf93f0936ddb80a7a0289b6672385779ed Mon Sep 17 00:00:00 2001 From: Ihar Tsykala Date: Wed, 6 May 2026 16:53:53 +0500 Subject: [PATCH 06/10] feat: 5911 Add output-reference selection to policy test automation --- .../policy-test-automation-draft.service.ts | 27 +++++++++++ ...olicy-test-automation-popup.component.html | 24 ++++++++-- ...olicy-test-automation-popup.component.scss | 45 +++++++++++++++++++ .../policy-test-automation-popup.component.ts | 4 ++ 4 files changed, 97 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts index a6f3155c19..a1dce6c175 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts @@ -105,6 +105,33 @@ export class PolicyTestAutomationDraftService { }); } + public confirmOutputFromInput(): void { + const input = this.draft.input; + if (!input) { + return; + } + this.addOutput({ + type: input.blockType || 'input', + id: [ + input.policyId, + input.blockId, + input.capturedAt + ].join(':'), + title: input.title || 'Confirmed output', + source: { + policyId: input.policyId, + blockId: input.blockId, + blockType: input.blockType, + inputCapturedAt: input.capturedAt + } + }); + this.update({ + input: null, + captureNextFormSubmit: false, + readyToSave: false + }); + } + public setMetadata(name: string, description: string): void { this.update({ name, description }); } diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html index c1250d73da..52e75c1190 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html @@ -22,15 +22,33 @@ Data on next form submission will be used for test input + + + +
Input captured
-
AWAITING OUTPUTS
-
+
Outputs
-
Confirm Document Results
+
+ {{ output.title || output.type }} +
+
No outputs yet
diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss index 5cab394a9b..41354a5cd2 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss @@ -78,3 +78,48 @@ line-height: 1.4; } } + +.policy-test-status-button { + width: 100%; + padding: 8px 12px; + border: 1px solid #ddd; + background: #f5f5f5; + color: #999; + font-size: 12px; + font-weight: 600; + border-radius: 4px; + cursor: default; + margin-bottom: 6px; + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } +} + +.policy-test-status-button-active { + border: 1px solid var(--color-primary, #3f51b5); + background: var(--color-primary, #3f51b5); + color: white; + cursor: pointer; + + &:hover { + opacity: 0.9; + } + + &:active { + opacity: 0.8; + } +} + +.policy-test-draft-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 8px; + background: #f9f9f9; + border: 1px solid #eee; + border-radius: 3px; + margin-bottom: 6px; + font-size: 11px; +} diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts index 2cf60aca63..082eea140a 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts @@ -16,4 +16,8 @@ export class PolicyTestAutomationPopupComponent { public discardInput(): void { this.draftService.discardInput(); } + + public onAwaitingOutputsClick(): void { + this.draftService.confirmOutputFromInput(); + } } From 9cb696e356dbe5748ba0d8fe740288a17de00613 Mon Sep 17 00:00:00 2001 From: Ihar Tsykala Date: Wed, 6 May 2026 18:12:10 +0500 Subject: [PATCH 07/10] feat: 5911 Add inspect data and discard actions to policy test draft --- .../policy-test-automation-draft.service.ts | 3 ++ ...olicy-test-automation-popup.component.html | 9 +++-- ...olicy-test-automation-popup.component.scss | 15 ++++++- .../policy-test-automation-popup.component.ts | 39 ++++++++++++++++++- 4 files changed, 59 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts index a1dce6c175..0a1935d4c0 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts @@ -26,6 +26,9 @@ export interface PolicyTestOutputAnchor { schemaId?: string; messageId?: string; rowId?: string; + blockId?: string; + blockType?: string; + inputCapturedAt?: string; }; capturedAt: string; } diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html index 52e75c1190..7a65e1cdb0 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html @@ -38,9 +38,10 @@ AWAITING OUTPUTS -
-
Input captured
- +
+ {{ draft.input.title || 'Input captured' }} + +
@@ -48,6 +49,8 @@
Outputs
{{ output.title || output.type }} + +
No outputs yet
diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss index 41354a5cd2..0d909db91e 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss @@ -113,8 +113,9 @@ } .policy-test-draft-item { - display: flex; - justify-content: space-between; + display: grid; + grid-template-columns: 1fr auto auto; + gap: 8px; align-items: center; padding: 6px 8px; background: #f9f9f9; @@ -122,4 +123,14 @@ border-radius: 3px; margin-bottom: 6px; font-size: 11px; + min-height: 28px; +} + +.policy-test-draft-item button { + border: 0; + background: none; + color: var(--color-primary, #3f51b5); + cursor: pointer; + padding: 0; + font-size: 12px; } diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts index 082eea140a..46e8259e89 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts @@ -1,5 +1,7 @@ import { Component } from '@angular/core'; -import { PolicyTestAutomationDraftService } from './policy-test-automation-draft.service'; +import { DialogService } from 'primeng/dynamicdialog'; +import { ViewerDialog } from '../../dialogs/viewer-dialog/viewer-dialog.component'; +import { PolicyTestAutomationDraftService, PolicyTestOutputAnchor } from './policy-test-automation-draft.service'; @Component({ selector: 'policy-test-automation-popup', @@ -7,7 +9,10 @@ import { PolicyTestAutomationDraftService } from './policy-test-automation-draft styleUrls: ['./policy-test-automation-popup.component.scss'] }) export class PolicyTestAutomationPopupComponent { - constructor(public draftService: PolicyTestAutomationDraftService) {} + constructor( + public draftService: PolicyTestAutomationDraftService, + private dialogService: DialogService + ) {} public onCaptureChange(checked: boolean): void { this.draftService.setCaptureNextFormSubmit(checked); @@ -20,4 +25,34 @@ export class PolicyTestAutomationPopupComponent { public onAwaitingOutputsClick(): void { this.draftService.confirmOutputFromInput(); } + + public inspectInput(): void { + const input = this.draftService.draft.input; + if (!input) { + return; + } + this.openJson(input.title || 'Input', input.document); + } + + public inspectOutput(output: PolicyTestOutputAnchor): void { + this.openJson(output.title || 'Output reference', output); + } + + public discardOutput(type: string, id: string): void { + this.draftService.discardOutput(type, id); + } + + private openJson(title: string, value: unknown): void { + this.dialogService.open(ViewerDialog, { + showHeader: false, + width: '90%', + styleClass: 'guardian-dialog', + data: { + title, + type: 'JSON', + value, + dryRun: true + } + }); + } } From 3aef1ddb143a360c98061f4b0190ea95b6d198e8 Mon Sep 17 00:00:00 2001 From: Ihar Tsykala Date: Wed, 6 May 2026 20:57:58 +0500 Subject: [PATCH 08/10] fix: 5911 ui popup --- ...olicy-test-automation-popup.component.html | 82 ++++++---- ...olicy-test-automation-popup.component.scss | 141 +++++++++++------- 2 files changed, 136 insertions(+), 87 deletions(-) diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html index 7a65e1cdb0..dcba2c9265 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html @@ -1,26 +1,6 @@
-
Policy Test Automation
- -
-
Input
- - - -
- AWAITING INPUT -
- -
- - Data on next form submission will be used for test input -
+
+
Policy Test Automation
+
-
- {{ draft.input.title || 'Input captured' }} - - +
+
Input
+ +
+
+ + +
+ {{ draft.input.title || 'Input captured' }} + + +
+
+ +
+
+ + Data on next form submission will be used for test input +
+
-
+
Outputs
-
- {{ output.title || output.type }} - - + +
+
+
Confirm Document Results
+ +
+ {{ output.title || output.type }} + + +
+
No outputs yet
+
+ +
+
+ + Review Document output and confirm they are correct +
+
-
No outputs yet
diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss index 0d909db91e..1043772508 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.scss @@ -1,77 +1,103 @@ .policy-test-automation-popup { - width: 280px; - padding: 12px; + width: 480px; + padding: 16px; color: #23252e; - font-size: 12px; + font-size: 13px; + border: 1px solid #999; + border-radius: 6px; + background: #d9d9d9; +} + +.policy-test-automation-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + margin-bottom: 16px; } .policy-test-automation-title { - font-weight: 600; - margin-bottom: 12px; + font-weight: 700; + font-size: 14px; } .policy-test-section { - margin-bottom: 12px; + margin-bottom: 16px; } .policy-test-section-title { - font-weight: 600; - margin-bottom: 6px; + font-weight: 700; + margin-bottom: 8px; + border-bottom: 1px solid #333; + padding-bottom: 2px; + display: inline-block; + text-decoration: underline; } -.policy-test-checkbox-row { +.policy-test-section-body { + display: grid; + grid-template-columns: auto 1fr; + gap: 16px; + align-items: start; +} + +.policy-test-section-left { display: flex; - align-items: center; + flex-direction: column; gap: 8px; } -.policy-test-section-disabled { - opacity: 0.45; +.policy-test-section-right { + display: flex; + flex-direction: column; + gap: 8px; } -.policy-test-link-button { - border: 0; - background: none; - color: var(--color-primary, #3f51b5); - cursor: pointer; - padding: 0; +.policy-test-section-subtitle { + font-style: italic; + color: #666; + font-size: 12px; } -.policy-test-captured-input { +.policy-test-checkbox-row { display: flex; - flex-direction: column; - gap: 6px; + align-items: center; + gap: 8px; + font-style: italic; } -.policy-test-input-name { - font-weight: 600; +.policy-test-checkbox-row input[type="checkbox"] { + width: 16px; + height: 16px; + cursor: pointer; } -.policy-test-muted { - color: #999; - font-size: 11px; +.policy-test-section-disabled { + opacity: 0.5; } -.policy-test-awaiting { - color: #f39c12; - font-weight: 600; - margin-bottom: 6px; +.policy-test-muted { + color: #888; + font-size: 12px; + font-style: italic; } .policy-test-help { display: flex; align-items: flex-start; - gap: 6px; - background: #f0f0f0; - padding: 8px; + gap: 8px; + background: #e8f5e9; + border: 1px solid #a5d6a7; + padding: 8px 10px; border-radius: 4px; font-size: 11px; - color: #666; - margin-bottom: 6px; + color: #2d5016; i { margin-top: 1px; flex-shrink: 0; + color: #2d5016; + font-size: 14px; } span { @@ -80,26 +106,26 @@ } .policy-test-status-button { - width: 100%; - padding: 8px 12px; - border: 1px solid #ddd; - background: #f5f5f5; - color: #999; + padding: 8px 18px; + border: 1px solid #999; + background: #b8b8b8; + color: #fff; font-size: 12px; - font-weight: 600; - border-radius: 4px; + font-weight: 700; + border-radius: 3px; cursor: default; - margin-bottom: 6px; + letter-spacing: 0.5px; + white-space: nowrap; &:disabled { - opacity: 0.5; + opacity: 1; cursor: not-allowed; } } .policy-test-status-button-active { - border: 1px solid var(--color-primary, #3f51b5); - background: var(--color-primary, #3f51b5); + border: 1px solid #0089d0; + background: #0089d0; color: white; cursor: pointer; @@ -115,22 +141,27 @@ .policy-test-draft-item { display: grid; grid-template-columns: 1fr auto auto; - gap: 8px; + gap: 12px; align-items: center; - padding: 6px 8px; - background: #f9f9f9; - border: 1px solid #eee; - border-radius: 3px; - margin-bottom: 6px; - font-size: 11px; - min-height: 28px; + font-size: 12px; } .policy-test-draft-item button { border: 0; background: none; - color: var(--color-primary, #3f51b5); + display: flex; + align-items: center; + gap: 4px; + color: #333; cursor: pointer; padding: 0; font-size: 12px; + + i { + font-size: 14px; + } + + &:hover { + opacity: 0.7; + } } From 610f5c2eb698a3fdf13ac5024bfa4a4d88c29e38 Mon Sep 17 00:00:00 2001 From: Ihar Tsykala Date: Thu, 7 May 2026 01:44:47 +0500 Subject: [PATCH 09/10] feat: 6021 warn if stop without outputs --- .../policy-test-automation-draft.service.ts | 1 + .../policy-test-automation-popup.component.html | 2 +- .../policy-test-automation-popup.component.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts index 0a1935d4c0..d353a9ee36 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-draft.service.ts @@ -121,6 +121,7 @@ export class PolicyTestAutomationDraftService { input.capturedAt ].join(':'), title: input.title || 'Confirmed output', + document: input.document, source: { policyId: input.policyId, blockId: input.blockId, diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html index dcba2c9265..b6ed31cecc 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.html @@ -49,7 +49,7 @@
-
+
Outputs
diff --git a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts index 46e8259e89..7939dcacc9 100644 --- a/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts +++ b/frontend/src/app/modules/policy-engine/policy-viewer/policy-test-automation/policy-test-automation-popup.component.ts @@ -35,7 +35,7 @@ export class PolicyTestAutomationPopupComponent { } public inspectOutput(output: PolicyTestOutputAnchor): void { - this.openJson(output.title || 'Output reference', output); + this.openJson(output.title || 'Output', output.document || output); } public discardOutput(type: string, id: string): void { From 5a16d98307e74b02ce2e5314649558ea17cca87e Mon Sep 17 00:00:00 2001 From: Ihar Tsykala Date: Thu, 7 May 2026 02:23:55 +0500 Subject: [PATCH 10/10] feat: 6021 Add warning dialog when stopping recording without outputs --- .../record-controller.component.ts | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.ts b/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.ts index 1e1f261f4f..c8d3a01483 100644 --- a/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.ts +++ b/frontend/src/app/modules/policy-engine/record/record-controller/record-controller.component.ts @@ -11,6 +11,7 @@ import { ImportEntityDialog, ImportEntityType } from 'src/app/modules/common/import-entity-dialog/import-entity-dialog.component'; +import {PolicyTestAutomationDraftService} from '../../policy-viewer/policy-test-automation/policy-test-automation-draft.service'; @Component({ selector: 'app-record-controller', @@ -47,6 +48,7 @@ export class RecordControllerComponent implements OnInit { private recordService: RecordService, private router: Router, private dialog: DialogService, + private policyTestDraft: PolicyTestAutomationDraftService, ) { this._showActions = (localStorage.getItem('SHOW_RECORD_ACTIONS') || 'true') === 'true'; this._overlay = localStorage.getItem('HIDE_RECORD_OVERLAY'); @@ -121,6 +123,38 @@ export class RecordControllerComponent implements OnInit { } public stopRecording() { + if (this.policyTestDraft.shouldWarnBeforeStop()) { + this.openNoOutputWarning(); + return; + } + + this.stopRecordingInternal(); + } + + private openNoOutputWarning(): void { + const dialogRef = this.dialog.open(ConfirmDialog, { + width: '560px', + data: { + title: 'Stop and Discard Input?', + description: [ + 'Input test data was not captured or there is no corresponding output document(s) selected.', + 'Stop this recording and discard input test data?' + ], + submitButton: 'Confirm', + cancelButton: 'Cancel' + }, + modal: true, + closable: false + }); + + dialogRef.onClose.subscribe((confirmed: boolean) => { + if (confirmed) { + this.stopRecordingInternal(); + } + }); + } + + private stopRecordingInternal(): void { this.loading = true; this.recordItems = []; this.recordService.stopRecording(this.policyId).subscribe((fileBuffer) => {