fix(frontend): preserve operator state border on workflow page return - #5146
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #5146 +/- ##
============================================
+ Coverage 50.29% 50.89% +0.59%
+ Complexity 2417 2378 -39
============================================
Files 1054 1053 -1
Lines 40905 40501 -404
Branches 4378 4314 -64
============================================
+ Hits 20575 20612 +37
+ Misses 19100 18700 -400
+ Partials 1230 1189 -41
*This pull request uses carry forward flags. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Codecov flagged 0% patch because my tests are in workflow-editor.component.spec.ts, which is excluded from the unit-test runner in angular.json. I'm planning to move them into a new sibling file workflow-editor-status-restoration.spec.ts so the default glob picks them up and was verified locally that this brings patch coverage close to 100%. Happy to take a different approach if the team prefers, otherwise can push the new planned spec.ts file. |
|
For UI/user facing changes, please include before and after screenshots in the PR description, under |
|
Also please add tests to cover your changes. |
|
@Yicong-Huang I have added a comment above regarding the test file & code coverage. |
just saw it, sorry. sg, you can place the tests else where for now, or simply add the tests in the same file and comment out the previous tests. |
No worries. Thanks for the suggestions. |
Quick clarification on Option 2, since workflow-editor.component.spec.ts is currently in angular.json's exclude list, commenting out the previous tests by itself wouldn't make the file run in CI (so Codecov would still report 0% on the new tests). Were you implicitly suggesting I also remove the exclusion as part of this, or would you prefer that change be left out of this PR? |
|
you can
Alternatively you can use another PR to enable this workflow-editor.component.spec.ts and uncomment and fix the previous test cases first. |
Should I wait for this PR to be reviewed and merged first before raising the follow-up PR to uncomment (and fix) the test cases in workflow-editor.component.spec.ts? |
/request-review @Yicong-Huang I shall raise the follow-up PR as soon as this is merged. Thanks. |
|
I added @Xiao-zhen-Liu to be a reviewer, assuming @Yicong-Huang may not be very available. My preference is that if the test cases are not related to this functionality of this PR, it should be fixed in a separate PR. |
|
as we are low on coverage, I would encourage to add test cases to related files when fit, even it is not fully related to the changed line. Otherwise we won't know if the change in this PR would break some related parts (because we don't have tests for them). After we have reached to a good ratio of coverage, then we can make it strict that only introduce or change tests related to the changed lines. |
There was a problem hiding this comment.
Thanks for tackling #3614 — the writeup of what was going wrong is excellent, and the actual fix (the two changes in workflow-editor.component.ts) is small, focused, and fixes a real problem. Two things worth addressing, both about how the border color is decided and how the new tests check it.
How the border color gets decided
- The underlying problem is that two separate parts of the code both set the operator's border color, and they were overwriting each other. This fix makes them agree in the normal case (a valid operator with a saved "completed" status), which is correct. But they are still two separate parts setting the same color, with the same look-up-and-repaint logic duplicated in both, and one tricky case remains: an operator that is both invalid AND has a saved "completed" status. Red (invalid) is supposed to win, but whether it actually does depends on which of the two code paths runs last — and nothing guarantees that order. It works today, but it's fragile. Having a single place decide the final color (check validity first, then saved status, then fall back to the default) would make this reliable and remove the duplicated logic that now lives in both places.
The new tests
- The new tests check "was a particular paint function called?" rather than "what color did the operator actually end up?". Because the same paint function is also called from the other new code path, one of the tests would pass even if the fix were removed, and another doesn't confirm the final color actually wins. Checking the operator's final border color directly would make the tests genuinely verify the behavior. See the inline notes on the two tests.
Xiao-zhen-Liu
left a comment
There was a problem hiding this comment.
Requesting changes — I think two of the points raised are worth addressing before merge rather than leaving as follow-ups:
-
Decide the operator's border color in one place. Right now two separate parts of the code set it, with the same look-up-and-repaint logic duplicated in both. Consolidating it into a single decision (check validity first, then saved status, then the default) removes the duplication and also fixes the edge case where an operator that is both invalid and has a saved "completed" status can show the wrong color depending on which code path happens to run last.
-
Make the two new tests check the operator's final border color rather than just whether a paint function was called. As written, one of them would pass even if the fix were removed, so the regression tests don't yet guard against the regression.
Details are in the inline comments. The behavior fix itself is correct and close — these are about making it robust and properly tested.
@Xiao-zhen-Liu Extracted applyOperatorBorder(operatorID) as the single decision point, validity -> cached execution status -> default valid. Both handleOperatorValidation and the operator-add subscription delegate to it. The invalid + cached Completed case is now resolved structurally (the helper picks red unconditionally for invalid), so the outcome no longer depends on which stream fires last. |
Signed-off-by: Prateek Ganigi <91584519+PG1204@users.noreply.github.com>
@Xiao-zhen-Liu Move the 6 mouse-event tests into a new sibling file workflow-editor.browser.spec.ts (uncommented), with its own TestBed setup. |
Can you move this discussion as a new issue if it is not going to be part of this PR? |
Xiao-zhen-Liu
left a comment
There was a problem hiding this comment.
LGTM — both points addressed: border color is now decided in one place (applyOperatorBorder), and the tests assert the final border color. Nice work.
Optional, non-blocking: applyOperatorBorder re-runs validateOperator instead of reusing the validation stream's result — negligible, fine to leave or tidy later.
Filed as #5318. Requesting your input on the same. Accordingly I shall raise the PR. |
…pache#5626) ### What changes were proposed in this PR? Addresses the two review comments on [apache#5318](apache#5318 follow-up implementation: 1. Share a common TestBed setup so the jsdom and browser specs don't drift. The .browser.spec.ts split created two TestBed configurations for the same component - one in workflow-editor.component.spec.ts (External Module Integration describe), one in workflow-editor.browser.spec.ts. Both configured nearly identical imports/providers arrays, which would inevitably drift over time. Extracted them into a new sibling file workflow-editor.test-utils.ts exporting two arrays: export const workflowEditorTestImports = [ ... ]; export const workflowEditorTestProviders: Provider[] = [ ... ]; This follows the existing project convention in [frontend/src/app/common/testing/test-utils.ts](vscode-webview://1epki5h79lmkghv36u4evg54fuvmk17ndjca203mcg7opnnd5sg9/frontend/src/app/common/testing/test-utils.ts) (which exports commonTestImports and commonTestProviders the same way). Each spec's TestBed now collapses to: await TestBed.configureTestingModule({ imports: workflowEditorTestImports, providers: workflowEditorTestProviders, }).compileComponents(); Adding or removing a service from either spec's setup is now a single edit in one file. 2. Drop the explicit workflow-editor.component.spec.ts entry from the test-browser include in angular.json. With the six mouse-event tests now living in workflow-editor.browser.spec.ts (picked up by the **/*.browser.spec.ts glob), the explicit listing of workflow-editor.component.spec.ts in test-browser's include array was causing the file's 25 jsdom-friendly tests to run twice, once in jsdom (the default test target) and once in real Chrome (the test-browser target). Removed that explicit entry; the test-browser target now picks up only **/*.browser.spec.ts files. Scope note. The pre-existing JointJS Paper describe block at the top of workflow-editor.component.spec.ts has its own deliberately-different TestBed setup (ContextMenuComponent instead of NzModalCommentBoxComponent, Overlay, MockComputingUnitStatusService) and was left untouched, it wasn't part of the duplication introduced by the .browser.spec.ts split. ### Any related issues, documentation, discussions? Addresses review feedback on the follow-up PR for [apache#5318](apache#5318). Closes apache#5318 Related to apache#3614 / PR apache#5146 (this PR restores tests that were commented out during PR apache#5146 as collateral from the apache#3614 fix). ### How was this PR tested? Existing test runs: Verified no test-count regressions in either target, and the double-run is gone: ng test (jsdom, full suite): 947 pass / 0 fail / 2 skipped / 1 todo across 103 files ng run gui:test-browser: 13 pass / 0 fail across 2 files (6 from workflow-editor.browser.spec.ts + 7 from the pre-existing code-editor.component.browser.spec.ts) workflow-editor.component.spec.ts under jsdom: 25/25 pass (unchanged from before this PR) Browser test count dropped from 38 -> 13, confirming the 25 jsdom-friendly tests in workflow-editor.component.spec.ts no longer double-run in real Chrome. Static checks: tsc --noEmit and eslint clean on all four changed files. ### Was this PR authored or co-authored using generative AI tooling? Co-authored-by: Claude Code (Anthropic Claude Opus 4.7) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…apache#5146) ### What changes were proposed in this PR? Fixes a visual regression where an operator's border, state text, port row counts, and worker count reset to default after navigating away from a workflow page and returning, even though the execution state is still cached in WorkflowStatusService. Root cause: WorkspaceComponent clears the workflow on destroy and calls reloadWorkflow() on re-init, recreating every operator from the workflow JSON with default JointJS attributes. The cached execution status was never reapplied, and the validation pass that runs on operator-add called changeOperatorColor(..., true) for valid operators, overwriting rect.body/stroke and forcing the border back to gray. Fix (two changes, both in workflow-editor.component.ts): Subscribe to getOperatorAddStream() inside handleOperatorStatisticsUpdate. When an operator is added (drag-drop, reloadWorkflow, undo/redo, collaborative add via Yjs - all routed through a single emission point), look up the cached OperatorStatistics. If present, call changeOperatorStatistics(...) to restore the state color, port labels, worker count, and state text. New operators with no cached entry early-return and get default coloring. Make handleOperatorValidation status-aware. Invalid operators still get a red border (priority preserved). For valid operators, the handler now checks the cached status - if one exists, it repaints via changeOperatorState(...) instead of overwriting with default gray. Valid operators with no cached status continue to get the default gray border. Before fix: https://github.com/user-attachments/assets/c0feadeb-2310-486b-93b2-39389635c67f After fix: https://github.com/user-attachments/assets/a709aff5-0376-4bb8-8053-185f9d5d790d ### Any related issues, documentation, discussions? Fixes apache#3614. ### How was this PR tested? Unit tests: Three tests added to workflow-editor.component.spec.ts under a new describe("operator border restoration after navigation") block: Valid operator + cached Completed -> changeOperatorState(..., Completed) is called. Valid operator + empty cache -> default changeOperatorColor(..., true) is called (existing behavior preserved). Invalid operator + cached Completed -> changeOperatorColor(..., false) is called, red wins (existing behavior preserved). All three pass under Vitest (ng test). Manual UI testing: Reproduced the issue's recording locally: Open a workflow (e.g., CSV File Scan → Radar Chart) and run it; both operators turn green with port row counts. Navigate to a different page, then back. Before fix: operators reset to default gray borders, row counts blank. After fix: green borders, row counts, worker counts, and "Completed" state label all persist. Edge cases manually verified: fresh workflow (default coloring), new operator dragged in after a completed run (default for new, cached for existing), re-running (resetStatus repaints Uninitialized, live updates flow normally), invalid operator with cached Completed (red border, validation priority). Note: workflow-editor.component.spec.ts was previously excluded from the default jsdom test target in angular.json. This PR removes that exclusion (so the new tests run in CI and contribute to Codecov coverage) and comments out six pre-existing mouse-event tests that fail under jsdom (they continue to pass in the ng run gui:test-browser target, marked with TODO(apache#3614) in place). A follow-up PR will revive those commented-out tests. ### Was this PR authored or co-authored using generative AI tooling? Co-authored-by: Claude Code (Anthropic Claude Opus 4.7) --------- Signed-off-by: Prateek Ganigi <91584519+PG1204@users.noreply.github.com>
…pache#5626) ### What changes were proposed in this PR? Addresses the two review comments on [apache#5318](apache#5318 follow-up implementation: 1. Share a common TestBed setup so the jsdom and browser specs don't drift. The .browser.spec.ts split created two TestBed configurations for the same component - one in workflow-editor.component.spec.ts (External Module Integration describe), one in workflow-editor.browser.spec.ts. Both configured nearly identical imports/providers arrays, which would inevitably drift over time. Extracted them into a new sibling file workflow-editor.test-utils.ts exporting two arrays: export const workflowEditorTestImports = [ ... ]; export const workflowEditorTestProviders: Provider[] = [ ... ]; This follows the existing project convention in [frontend/src/app/common/testing/test-utils.ts](vscode-webview://1epki5h79lmkghv36u4evg54fuvmk17ndjca203mcg7opnnd5sg9/frontend/src/app/common/testing/test-utils.ts) (which exports commonTestImports and commonTestProviders the same way). Each spec's TestBed now collapses to: await TestBed.configureTestingModule({ imports: workflowEditorTestImports, providers: workflowEditorTestProviders, }).compileComponents(); Adding or removing a service from either spec's setup is now a single edit in one file. 2. Drop the explicit workflow-editor.component.spec.ts entry from the test-browser include in angular.json. With the six mouse-event tests now living in workflow-editor.browser.spec.ts (picked up by the **/*.browser.spec.ts glob), the explicit listing of workflow-editor.component.spec.ts in test-browser's include array was causing the file's 25 jsdom-friendly tests to run twice, once in jsdom (the default test target) and once in real Chrome (the test-browser target). Removed that explicit entry; the test-browser target now picks up only **/*.browser.spec.ts files. Scope note. The pre-existing JointJS Paper describe block at the top of workflow-editor.component.spec.ts has its own deliberately-different TestBed setup (ContextMenuComponent instead of NzModalCommentBoxComponent, Overlay, MockComputingUnitStatusService) and was left untouched, it wasn't part of the duplication introduced by the .browser.spec.ts split. ### Any related issues, documentation, discussions? Addresses review feedback on the follow-up PR for [apache#5318](apache#5318). Closes apache#5318 Related to apache#3614 / PR apache#5146 (this PR restores tests that were commented out during PR apache#5146 as collateral from the apache#3614 fix). ### How was this PR tested? Existing test runs: Verified no test-count regressions in either target, and the double-run is gone: ng test (jsdom, full suite): 947 pass / 0 fail / 2 skipped / 1 todo across 103 files ng run gui:test-browser: 13 pass / 0 fail across 2 files (6 from workflow-editor.browser.spec.ts + 7 from the pre-existing code-editor.component.browser.spec.ts) workflow-editor.component.spec.ts under jsdom: 25/25 pass (unchanged from before this PR) Browser test count dropped from 38 -> 13, confirming the 25 jsdom-friendly tests in workflow-editor.component.spec.ts no longer double-run in real Chrome. Static checks: tsc --noEmit and eslint clean on all four changed files. ### Was this PR authored or co-authored using generative AI tooling? Co-authored-by: Claude Code (Anthropic Claude Opus 4.7) --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ratorBorder (apache#5702) ### What changes were proposed in this PR? WorkflowEditorComponent.applyOperatorBorder(operatorID)` always recomputes validation as its first step: ```typescript const validation = this.validationWorkflowService.validateOperator(operatorID); ``` This is redundant when the helper is called from the validation-stream subscriber in handleOperatorValidation, the stream's emitted event already carries the Validation result that was just computed by updateValidationState. This PR: - Adds an optional validation?: Validation parameter to applyOperatorBorder. The helper uses it when provided, and falls back to validateOperator(operatorID) otherwise. - Updates handleOperatorValidation to pass value.validation from the stream into the helper. - Leaves the operator-add stream subscriber unchanged and hence it doesn't have a Validation in hand at that point, so it correctly falls through to the lazy-fetch path. - Functionally identical (the stream emits the same Validation that would have been recomputed), purely avoids the duplicate call. Originally flagged as an optional nit during the PR apache#5146 review. Attempted in PR apache#5626 but split out as per the reviewer's request so that PR could stay scoped to test restructuring; this PR completes the follow-up. ### Any related issues, documentation, discussions? Closes apache#5683 Related: PR apache#5146 (where the nit was raised), PR apache#5626 (where this was attempted and split out). ### How was this PR tested? Added one focused unit test in `workflow-editor.component.spec.ts` inside the existing `operator border restoration after navigation` describe block: `it("uses the Validation passed in instead of recomputing it", ...)`. The test clears the `validateOperator` spy after the operator-add validation chain settles, then calls `applyOperatorBorder` directly with a `Validation` argument and asserts `validateOperator` is not called. Verified locally: - `tsc --noEmit`: clean - `eslint`: clean - `ng test` (jsdom): 26/26 pass in the editor spec (was 25, new test adds one) - `ng run gui:test-browser`: 13/13 pass ### Was this PR authored or co-authored using generative AI tooling? This PR was co-authored using Claude Code (Anthropic Claude Opus 4.7)
…torBorder (apache#6075) ### What changes were proposed in this PR? Follow-up to apache#5702. `WorkflowEditorComponent.applyOperatorBorder` decides an operator's border color and has two callers: - `handleOperatorValidation`: the validation-stream subscriber, which already had the `Validation` and passed it in. - The operator-add stream subscriber: which passed no `Validation` and relied on an optional-parameter fallback that recomputed it inside the helper. This PR unifies the two paths so both callers obtain the `Validation` themselves and pass it in: - The operator-add subscriber now computes it via `validationWorkflowService.validateOperator(...)` and passes it, mirroring the validation-stream caller. - `applyOperatorBorder`'s `validation` parameter becomes **required**, and the `?? validateOperator(...)` fallback is removed. The color decision no longer silently depends on a recompute hidden inside the helper. This is a non-functional cleanup and no behavioral change. In particular, the operator-add subscriber still paints the border immediately (and restores cached run statistics), preserving the navigation-reset behavior from apache#3614. Note on scope: the issue's summary mentions "painted exactly once." Truly collapsing to a single paint conflicts with the apache#3614 requirement that borders render immediately on reload without waiting for validation events, and would need fragile "just-added" state tracking in the validation handler. This PR implements the safe, behavior-preserving unification (consistent validation acquisition, fallback removed) rather than suppressing either paint. ### Any related issues, documentation, discussions? Part of apache#5726. Related: apache#5702 (added the optional parameter; this issue arose from its review), apache#5146 (introduced `applyOperatorBorder` and the apache#3614 navigation fix). ### How was this PR tested? Updated `workflow-editor.component.spec.ts`: - Renamed the "uses the Validation passed in instead of recomputing it" test to "relies solely on the passed-in Validation (never recomputes inside the helper)": the old name implied a recompute path that no longer exists. - Added "supplies a computed Validation from the operator-add path", asserting the operator-add subscriber calls `applyOperatorBorder` with a `Validation` object. - Existing border-outcome tests (green / gray / red / invalid-over-cached priority) remain and continue to pass. Verified locally: - `tsc --noEmit`: clean - `prettier --check` / `eslint`: clean - `ng test` (jsdom): 30/30 pass in the editor spec - `ng run gui:test-browser`: 13/13 pass ### Was this PR authored or co-authored using generative AI tooling? Co-authored with Claude Opus 4.8 in compliance with ASF
What changes were proposed in this PR?
Fixes a visual regression where an operator's border, state text, port row counts, and worker count reset to default after navigating away from a workflow page and returning, even though the execution state is still cached in WorkflowStatusService.
Root cause: WorkspaceComponent clears the workflow on destroy and calls reloadWorkflow() on re-init, recreating every operator from the workflow JSON with default JointJS attributes. The cached execution status was never reapplied, and the validation pass that runs on operator-add called changeOperatorColor(..., true) for valid operators, overwriting rect.body/stroke and forcing the border back to gray.
Fix (two changes, both in workflow-editor.component.ts):
Subscribe to getOperatorAddStream() inside handleOperatorStatisticsUpdate. When an operator is added (drag-drop, reloadWorkflow, undo/redo, collaborative add via Yjs - all routed through a single emission point), look up the cached OperatorStatistics. If present, call changeOperatorStatistics(...) to restore the state color, port labels, worker count, and state text. New operators with no cached entry early-return and get default coloring.
Make handleOperatorValidation status-aware. Invalid operators still get a red border (priority preserved). For valid operators, the handler now checks the cached status - if one exists, it repaints via changeOperatorState(...) instead of overwriting with default gray. Valid operators with no cached status continue to get the default gray border.
Before fix:
473837274-d703b42e-8504-4735-bc9a-bcac9602c201.mov
After fix:
Screen.Recording.2026-05-22.at.4.18.14.PM.mov
Any related issues, documentation, discussions?
Fixes #3614.
How was this PR tested?
Unit tests: Three tests added to workflow-editor.component.spec.ts under a new describe("operator border restoration after navigation") block:
Valid operator + cached Completed -> changeOperatorState(..., Completed) is called.
Valid operator + empty cache -> default changeOperatorColor(..., true) is called (existing behavior preserved).
Invalid operator + cached Completed -> changeOperatorColor(..., false) is called, red wins (existing behavior preserved).
All three pass under Vitest (ng test).
Manual UI testing: Reproduced the issue's recording locally:
Open a workflow (e.g., CSV File Scan → Radar Chart) and run it; both operators turn green with port row counts.
Navigate to a different page, then back.
Before fix: operators reset to default gray borders, row counts blank. After fix: green borders, row counts, worker counts, and "Completed" state label all persist.
Edge cases manually verified: fresh workflow (default coloring), new operator dragged in after a completed run (default for new, cached for existing), re-running (resetStatus repaints Uninitialized, live updates flow normally), invalid operator with cached Completed (red border, validation priority).
Note: workflow-editor.component.spec.ts was previously excluded from the default jsdom test target in angular.json. This PR removes that exclusion (so the new tests run in CI and contribute to Codecov coverage) and comments out six pre-existing mouse-event tests that fail under jsdom (they continue to pass in the ng run gui:test-browser target, marked with TODO(#3614) in place). A follow-up PR will revive those commented-out tests.
Was this PR authored or co-authored using generative AI tooling?
Co-authored-by: Claude Code (Anthropic Claude Opus 4.7)