import { EventEmitter, OnInit, OnChanges, SimpleChanges, ElementRef, ChangeDetectorRef } from '@angular/core'; import { FormBuilder, FormGroup } from '@angular/forms'; import { DndDropEvent } from 'ngx-drag-drop'; import { DynamicSelectFieldConfig, SelectOption } from '../../dynamic-select/dynamic-select-field.component'; import { LoopStepConfig, TestCaseStepConfig, NormalStepConfig, ConditionStepConfig, StepGroupConfig, ConditionBranch } from '../test-case-step.models'; import { DataProfileOption } from '../../step-builder/step-builder-loop/step-builder-loop.component'; import { ViewDetailsPayload } from '../step-details-modal/step-details-modal-data'; import { CqaAutocompleteOption } from '../../autocomplete/autocomplete.model'; import * as i0 from "@angular/core"; export declare class TestCaseLoopStepComponent implements OnInit, OnChanges { private fb; private cdr; config: LoopStepConfig; id: string; loopType: 'for' | 'while'; stepNumber: number | string; /** Array index from parent renderer - use this for editInDepth instead of stepNumber (stepNumber "10.3" parses to 10) */ index?: number; condition?: string; maxIterations?: number; testDataProfile?: string; startStep?: number; endStep?: number; nestedSteps: TestCaseStepConfig[]; expanded: boolean; isNested: boolean; isInsideLoop: boolean; /** True when this step is nested (at any depth) inside another FOR loop. Used to keep * the test-data-profile dropdown enabled only for nested FOR loops; root-level FOR * loops auto-inherit the test case's default profile and are disabled. */ isInsideForLoop: boolean; isInsideStepGroup: boolean; isReorder: boolean; /** True when any ancestor step is skipped; used to cascade skipped styling to nested rows. */ isParentSkipped: boolean; selected: boolean; isDuplicating: boolean; /** Options for the data profile dropdown - accepts DataProfileOption objects */ dataProfileOptions: DataProfileOption[]; /** Indicates if more data profiles are available for loading */ hasMoreDataProfiles: boolean; /** Loading state for data profiles */ isLoadingDataProfiles: boolean; /** Options for natural text actions (for while loop condition) */ naturalTextActionsOptions: SelectOption[]; /** Passed to nested renderers (e.g. reusable step group hint). */ currentTestCaseId?: number | string; /** The default test data profile ID configured on the test case (testCase.testDataId). When the * FOR loop is at root level (not nested inside another FOR loop), the dropdown will be auto-set * to this id and disabled. */ testCaseTestDataId?: number | string; /** Function to process template variables for while condition (same as condition-step) */ setConditionTemplateVariables: (template: any) => any[]; /** Show "Add Step Between" button above nested steps */ addStepBetweenAbove: boolean; /** Show "Add Step Between" button below nested steps */ addStepBetweenBelow: boolean; /** @deprecated Use addStepBetweenAbove and addStepBetweenBelow instead. If set, applies to both buttons. */ addStepBetween: boolean; editable: boolean; /** Visual indentation level for nested structures (passed down from renderer). */ nestingLevel: number; toggleExpanded: EventEmitter<{ config: LoopStepConfig; expanded: boolean; }>; testDataProfileChange: EventEmitter; startStepChange: EventEmitter; endStepChange: EventEmitter; conditionChange: EventEmitter; maxIterationsChange: EventEmitter; nestedStepChange: EventEmitter<{ step: TestCaseStepConfig; index: number; }>; /** Emits void when add is from nested content; emits { loopStepConfig } when empty-state add is clicked (same behaviour as add-step-between). */ addStep: EventEmitter; deleteStep: EventEmitter; duplicate: EventEmitter; delete: EventEmitter; moreOptions: EventEmitter; viewDetails: EventEmitter; editInDepth: EventEmitter<{ step: TestCaseStepConfig; index: number; templateOverride?: any; }>; edit: EventEmitter; dndDropInZone: EventEmitter<{ event: DndDropEvent; targetList: TestCaseStepConfig[]; }>; /** Emit when more data profiles need to be loaded */ loadMoreDataProfiles: EventEmitter; /** Emit when data profile search query changes */ searchDataProfiles: EventEmitter; /** Emit when this step (or nested steps) is updated. */ stepUpdate: EventEmitter; /** Re-emit when a nested step (e.g. normal-step) emits clickAction (e.g. element/label click for popup) */ clickAction: EventEmitter<{ event: MouseEvent; step: TestCaseStepConfig; }>; /** Re-emit addStepBetweenClick from nested renderers */ addStepBetweenClick: EventEmitter<{ step: TestCaseStepConfig; index: number; position: 'ABOVE' | 'BELOW'; }>; selectionChange: EventEmitter; deleteBranch: EventEmitter; /** Re-emit when nested condition step emits addStepForBranch (add step to empty IF/ELSE IF/ELSE branch). Required for condition inside loop. */ addStepForBranch: EventEmitter<{ branch: ConditionBranch; }>; /** Re-emit when nested condition step emits deleteStepWithBranch. Required for condition inside loop. */ deleteStepWithBranch: EventEmitter<{ branch: ConditionBranch; stepIndex: number; }>; /** Re-emit when nested step group emits openExternal (redirect to step group in new tab) */ openExternal: EventEmitter<{ step: TestCaseStepConfig; }>; onDndDrop(event: DndDropEvent): void; /** Emit clickAction when user clicks on loop display badges (element, test_data, etc.) so parent can open edit modals */ onLoopActionClick(event: MouseEvent): void; /** Forward clickAction from nested steps only when not inside a step-group (Element/Test Data modals disabled there). */ onNestedClickAction(payload: { event: MouseEvent; step: TestCaseStepConfig; }): void; /** Expose global constructors for template (Angular templates don't have String/Number) */ readonly stringFn: StringConstructor; readonly numberFn: NumberConstructor; /** When true, header shows inline edit form (dropdowns, inputs, Cancel/Apply) */ isEditing: boolean; /** Form for edit mode; bound to cqa-dynamic-select (same pattern as default stories) */ editForm: FormGroup; /** Cached select configs (stable refs to avoid change-detection loops) */ forOptionTypeSelectConfig: DynamicSelectFieldConfig; testDataProfileSelectConfig: DynamicSelectFieldConfig; startStepSelectConfig: DynamicSelectFieldConfig; endStepSelectConfig: DynamicSelectFieldConfig; conditionSelectConfig: DynamicSelectFieldConfig; /** While loop: config for autocomplete options (WHILE_CONDITION only) */ whileConditionSelectConfig: DynamicSelectFieldConfig; /** Selected template for while condition (same pattern as condition-step) */ selectedTemplate: any; /** Template variables for while condition */ templateVariables: any[]; /** Form group for template variables */ templateVariablesForm: FormGroup; /** Default max for Loop Start / End when no profile selected (options 1..10) */ private static readonly DEFAULT_LOOP_OPTIONS_MAX; viewDetailsTrigger?: ElementRef; /** Local copy of values while editing (for Cancel revert) */ private editSnapshot; constructor(fb: FormBuilder, cdr: ChangeDetectorRef); ngOnInit(): void; /** Base left padding for step rows (matches px-4 = 16px) plus 24px per nesting level (matches pl-10 = 40px at level 1). */ getRowPaddingLeftPx(): number; ngOnChanges(changes: SimpleChanges): void; /** Derive forOptionType for FOR loop from config so dropdown shows existing selection */ private getForOptionTypeFromConfig; /** Test Data Profile dropdown value: prefer forLoopTestDataId from config (or dataMapJson/dataMapBean). Use option.id when options loaded so type matches select. */ private getTestDataProfileFormValue; /** Loop Start dropdown value: prefer forLoopStartIndex from config (or dataMapJson/dataMapBean), else startStep */ private getLoopStartFormValue; /** Loop End dropdown value: prefer forLoopEndIndex from config (or dataMapJson/dataMapBean), else endStep */ private getLoopEndFormValue; private buildEditForm; /** * Populate condition, selectedTemplate and template variables from config.naturalTextActionId when options are available. * Returns { condition } if a match was found so buildEditForm can set initial form value. */ private populateWhileConditionFromNaturalTextActionId; /** Rebuild condition + template state when naturalTextActionsOptions load or config changes (same as condition-step). */ private rebuildWhileConditionFromNaturalTextAction; /** Build select configs once (same option shape as default stories: id + name); FOR loop adds selectedValue from step */ private buildSelectConfigs; /** Update condition select config with natural text actions options (legacy mat-select) */ private updateConditionSelectConfig; /** While loop: only show options with stepActionType === "WHILE_LOOP" */ private updateWhileConditionSelectConfig; /** Options for the while condition autocomplete (WHILE_LOOP natural text actions only) */ get whileConditionAutocompleteOptions(): (CqaAutocompleteOption & { template?: any; })[]; /** Display value for while condition autocomplete: show natural text from option matching naturalTextActionId when available (including when form is empty on open), else form value. When user has selected a different option, show form value. */ get whileConditionDisplayValue(): string; onConditionSelect(option: CqaAutocompleteOption & { template?: any; }): void; private buildTemplateVariablesForm; getSelectConfigForVariable(variable: any): DynamicSelectFieldConfig; /** Update test data profile select config with current options and selectedValue (forLoopTestDataId). Ensure selected id is in options so mat-select can display it. */ private updateTestDataProfileSelectConfig; /** Update Loop Start / Loop End select configs with selectedValue (forLoopStartIndex, forLoopEndIndex) so dropdowns show selected */ private updateStartEndStepSelectConfigsSelectedValues; /** Update all FOR loop select configs (testDataProfile, startStep, endStep) with selectedValue from step so dropdowns show selected */ private updateForLoopSelectConfigsSelectedValues; /** Convert DataProfileOption[] to SelectOption[] */ private convertDataProfileOptionsToSelectOptions; /** Best-known persisted label for the selected test data profile, even when current search results exclude it. */ private getStoredTestDataProfileLabel; /** Resolve a data profile id/value back to the display name used by the dropdown trigger. */ private resolveTestDataProfileName; /** Data length (N) of the currently selected test data profile; 0 if none selected or not found. */ private getSelectedProfileDataLength; /** Loop Start/End options: 1 to N based on selected data profile's data array length; falls back to 1..10 if no profile. */ getLoopStepOptions(): number[]; /** Sentinel value for "Start" / "End" options (same as old UI). */ static readonly LOOP_INDEX_START = -1; static readonly LOOP_INDEX_END = -1; /** Loop Start/End select options: same indices as getLoopStepOptions(), with label "name (index)" when profile data has row names, else "Step (index)". */ private getLoopStepSelectOptions; /** Loop Start dropdown options: "Start" (value -1) first, then data rows. Same values as old UI. */ private getLoopStartSelectOptions; /** Loop End dropdown options: data rows first, then "End" (value -1) last. Same values as old UI. */ private getLoopEndSelectOptions; /** Handle data profile selection: update display and reset start/end like the old UI. */ private onDataProfileChange; /** Handle search for data profiles */ private onSearchDataProfiles; /** Handle load more data profiles */ private onLoadMoreDataProfiles; onEditFormFieldChange(controlName: string, value: string | number): void; getLoopTypeLabel(): string; isWhileLoop(): boolean; isForLoop(): boolean; getStartLabel(): string; getEndLabel(): string; /** START loop body marker: first nested step's step number, or the loop row's step number if empty. */ getLoopMarkerStartStepDisplay(): string; /** END loop body marker: last nested step's step number, or the loop row's step number if empty. */ getLoopMarkerEndStepDisplay(): string; /** Resolve display start index: prefer forLoopStartIndex from step (or dataMapJson/dataMapBean), else startStep, else from nested steps */ private getDisplayStartIndex; /** Resolve display end index: prefer forLoopEndIndex from step (or dataMapJson/dataMapBean), else endStep, else from nested steps */ private getDisplayEndIndex; /** Resolve display profile name: map forLoopTestDataId to profile name from dataProfileOptions, else use testDataProfile */ private getDisplayTestDataProfileName; /** Get the selected data profile (by forLoopTestDataId or form value), or null. */ private getSelectedDataProfile; /** Get display name for a 1-based row index from the selected profile's data array (data[index-1].name). Returns null if no name. */ private getDataRowNameForOneBasedIndex; /** Whether test data profile is set (so we can show/hide the chip) */ hasTestDataProfile(): boolean; /** Whether start value is explicitly set (config or startStep input), not inferred from nested steps */ hasStartValue(): boolean; /** Whether end value is explicitly set (config or endStep input), not inferred from nested steps */ hasEndValue(): boolean; getStepsSummary(): string; getStartStepLabel(): string; getEndStepLabel(): string; getTestDataProfileLabel(): string; /** Test Data profile part only - for vertical stack layout (row 1) */ getForLoopProfileDisplay(): string; /** Start and End parts only - for vertical stack layout (row 2, stays together) */ getForLoopRangeDisplay(): string; private escapeForLoopHtml; /** Full display for while loop: substitutes #{variable} with HTML badges (element/test_data) so clicks open popup - max iterations shown as separate badge */ getConditionDisplay(): string; /** Substitutes #{variable} placeholders with HTML badge spans using config.event - enables click-to-edit popup like condition step */ private substituteTemplateVariablesToHtml; /** Event key-value pairs for display (e.g. selector, element) - same as condition step getBranchEventDetailsForDisplay */ getLoopEventDetailsForDisplay(): { key: string; value: string; }[]; onToggleExpanded(): void; onTestDataProfileChange(value: string): void; onStartStepChange(value: number): void; onEndStepChange(value: number): void; onConditionChange(value: string): void; onMaxIterationsChange(value: number): void; onNestedStepChange(step: TestCaseStepConfig, index: number): void; /** Re-emit openExternal from nested step group so redirect works for step groups inside loops. */ onNestedOpenExternal(payload: { step: TestCaseStepConfig; }): void; onNestedToggleExpanded(event: boolean | { config: TestCaseStepConfig; expanded: boolean; }, step: TestCaseStepConfig, index: number): void; onAddStep(): void; /** Re-emit addStepForLoop from nested loop so N-level nesting works (empty-state add step). */ onAddStepForLoopFromNested(event: { loopStepConfig: LoopStepConfig; }): void; /** Called from empty-state plus only; same behaviour as add-step-between (parentId = loop step id, position = parent + 1). */ onAddStepEmpty(): void; onDeleteStep(payloadOrIndex: any, index?: number): void; /** Re-emit edit from nested step so the portal can open edit modal (same as root level). */ onNestedEdit(event: { step: TestCaseStepConfig; index: number; }): void; /** Re-emit duplicate from nested step so the portal can duplicate (same as root level). */ onNestedDuplicate(event: any, step: TestCaseStepConfig): void; isNormalStep(step: TestCaseStepConfig): step is NormalStepConfig; isLoopStep(step: TestCaseStepConfig): step is LoopStepConfig; isConditionStep(step: TestCaseStepConfig): step is ConditionStepConfig; isStepGroup(step: TestCaseStepConfig): step is StepGroupConfig; onAddBranch(): void; onDeleteBranch(branch: any): void; onNestedConditionAddBranch(nestedStep: ConditionStepConfig, index: number): void; onNestedConditionDeleteBranch(nestedStep: ConditionStepConfig, branch: any, index: number): void; onOpenExternal(): void; /** Sync FOR loop edit form so dropdowns show existing config (forOptionType, forLoopTestDataId/testDataProfile, forLoopStartIndex/forLoopEndIndex) */ private syncForLoopEditFormFromConfig; onEdit(): void; onEditApply(): void; onEditCancel(): void; onEditInDepth(templateOverride?: any): void; onLink(): void; onDuplicate(): void; onDelete(): void; onSkipToggle(): void; onSelectionChange(checked: boolean): void; onMoreOptions(): void; onViewDetails(event?: MouseEvent): void; static ɵfac: i0.ɵɵFactoryDeclaration; static ɵcmp: i0.ɵɵComponentDeclaration; }