Task Summary
OperatorPropertyEditFrameComponent is at 74.76 % with 73 uncovered lines, and the largest block is setFormlyFormBinding's field-mapping chain — the long run of per-key rules that rewrite each Formly field config before the form renders. Two earlier passes (#6348, #6826) covered the component's other methods; this chain is what they left.
Each rule fires on a specific mappedField.key or operator type, so covering them is a matter of feeding schemas that contain those keys and asserting the produced field config. Pure EXTEND of operator-property-edit-frame.component.spec.ts.
Vitest/jsdom; see frontend/TESTING.md and frontend/AGENTS.md. Use the local coverage report to catch what is left — the lines below are where the report points today, not a fence.
Behavior to add
OperatorPropertyEditFrameComponent.setFormlyFormBinding (frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts, codecov 74.76 % — EXTEND)
Drive a schema through the binding and assert the resulting field config rather than calling the helpers directly:
- Dummy-field rules —
dummyOperator (812–816) sets templateOptions.disabled / readonly expressions; dummyProperty and dummyValue (821–825) set the same pair in the opposite order; dummyPropertyList (830–837) additionally sets hide from currentOperatorSchema?.operatorType !== "Dummy" and pins canRemove / canAdd to false. Cover each key, and cover dummyPropertyList with the operator type both equal to and different from "Dummy" so the hide expression is evaluated both ways.
- Type-substitution rules —
fileName (860–861) becomes type: "inputautocomplete", huggingFaceModel (864–865) becomes type: "huggingface". Assert the substituted type on the mapped field.
- Aggregate requirement (1073–1077) — when
currentOperatorSchema?.operatorType === "Aggregate" and the field is the aggregate attribute, an expression delegating to isAggregateAttributeRequired(field.parent?.model?.aggFunction) is installed. Cover an aggregate function that makes the attribute required and one that does not, and the non-Aggregate operator so the rule is skipped.
- Style overrides (780–806) — when
operatorPropertyDiff has an entry for the current operator, fieldStyleOverride is populated; a operatorVersion entry then writes the boundary style onto the element with class operator-version (786). That line indexes document.getElementsByClassName("operator-version")[0] directly, so the element must exist in the DOM or the test throws — attach the fixture to document.body, or insert the element, before exercising this path. Also cover the lookup at 799–806 that returns { style: … } for an overridden key and {} for a key with no override.
- Preset wrapper (1099–1109) — the
mappedField.type guard and the branch gated on config.env.userPresetEnabled that calls PresetWrapperComponent.setupFieldConfig. Cover the flag both on and off.
- Validator defaults (1140–1143, and the same shape at 1286–1287) — the
validators === undefined branch that initialises the object before attaching validation. Cover a field that already declares validators and one that does not.
- Interactivity (1310–1311) —
if (!this.interactive) fieldConfig?.form?.disable(). Cover both interactive and read-only frames.
- Schema-driven dependencies (1320–1333) —
if (schemaProperties && fields), the typeof propertyValue === "boolean" early return, the toggleHidden branch calling setHideExpression, and the dependOn branch which, when currentOperatorId is defined, pulls the input schema map and calls setChildTypeDependency. Cover a boolean property value, a toggleHidden property, and a dependOn property.
Determinism notes:
fixture.detectChanges() after each state change; attach the fixture to document.body for the style-override case so the operator-version element resolves.
- Stub
workflowCompilingService.getOperatorInputSchemaMap rather than reaching a backend.
- Assert on the mapped field object (
type, hide, expressions, validators) and on the result of calling an installed expression, not on its source text.
vi.restoreAllMocks() in afterEach; fixture.destroy() so untilDestroyed subscriptions stop.
- No layout or geometry assertions — setting an inline style attribute is fine to assert, measured geometry is not.
Out of scope: the Quill title-editing methods (connectQuillToText, disconnectQuillFromText, registerQuillBinding), which #6826 deliberately set aside.
Note open PRs touch this component (#7644, #7351, #7184, #6897) — rebase before finishing.
Task Type
Task Summary
OperatorPropertyEditFrameComponentis at 74.76 % with 73 uncovered lines, and the largest block issetFormlyFormBinding's field-mapping chain — the long run of per-key rules that rewrite each Formly field config before the form renders. Two earlier passes (#6348, #6826) covered the component's other methods; this chain is what they left.Each rule fires on a specific
mappedField.keyor operator type, so covering them is a matter of feeding schemas that contain those keys and asserting the produced field config. Pure EXTEND ofoperator-property-edit-frame.component.spec.ts.Vitest/jsdom; see
frontend/TESTING.mdandfrontend/AGENTS.md. Use the local coverage report to catch what is left — the lines below are where the report points today, not a fence.Behavior to add
OperatorPropertyEditFrameComponent.setFormlyFormBinding(frontend/src/app/workspace/component/property-editor/operator-property-edit-frame/operator-property-edit-frame.component.ts, codecov 74.76 % — EXTEND)Drive a schema through the binding and assert the resulting field config rather than calling the helpers directly:
dummyOperator(812–816) setstemplateOptions.disabled/readonlyexpressions;dummyPropertyanddummyValue(821–825) set the same pair in the opposite order;dummyPropertyList(830–837) additionally setshidefromcurrentOperatorSchema?.operatorType !== "Dummy"and pinscanRemove/canAddto false. Cover each key, and coverdummyPropertyListwith the operator type both equal to and different from"Dummy"so thehideexpression is evaluated both ways.fileName(860–861) becomestype: "inputautocomplete",huggingFaceModel(864–865) becomestype: "huggingface". Assert the substituted type on the mapped field.currentOperatorSchema?.operatorType === "Aggregate"and the field is the aggregate attribute, an expression delegating toisAggregateAttributeRequired(field.parent?.model?.aggFunction)is installed. Cover an aggregate function that makes the attribute required and one that does not, and the non-Aggregate operator so the rule is skipped.operatorPropertyDiffhas an entry for the current operator,fieldStyleOverrideis populated; aoperatorVersionentry then writes the boundary style onto the element with classoperator-version(786). That line indexesdocument.getElementsByClassName("operator-version")[0]directly, so the element must exist in the DOM or the test throws — attach the fixture todocument.body, or insert the element, before exercising this path. Also cover the lookup at 799–806 that returns{ style: … }for an overridden key and{}for a key with no override.mappedField.typeguard and the branch gated onconfig.env.userPresetEnabledthat callsPresetWrapperComponent.setupFieldConfig. Cover the flag both on and off.validators === undefinedbranch that initialises the object before attaching validation. Cover a field that already declares validators and one that does not.if (!this.interactive) fieldConfig?.form?.disable(). Cover both interactive and read-only frames.if (schemaProperties && fields), thetypeof propertyValue === "boolean"early return, thetoggleHiddenbranch callingsetHideExpression, and thedependOnbranch which, whencurrentOperatorIdis defined, pulls the input schema map and callssetChildTypeDependency. Cover a boolean property value, atoggleHiddenproperty, and adependOnproperty.Determinism notes:
fixture.detectChanges()after each state change; attach the fixture todocument.bodyfor the style-override case so theoperator-versionelement resolves.workflowCompilingService.getOperatorInputSchemaMaprather than reaching a backend.type,hide,expressions,validators) and on the result of calling an installed expression, not on its source text.vi.restoreAllMocks()inafterEach;fixture.destroy()sountilDestroyedsubscriptions stop.Out of scope: the Quill title-editing methods (
connectQuillToText,disconnectQuillFromText,registerQuillBinding), which #6826 deliberately set aside.Note open PRs touch this component (#7644, #7351, #7184, #6897) — rebase before finishing.
Task Type