Task Summary
MenuComponent's class file is at 86.27 % with 26 uncovered lines, concentrated in getRunButtonBehavior — the method that maps each execution state to the run/pause/resume button's label, icon and click handler. Two earlier passes (#7352, #7775) covered the operator-menu sub-component and the clipboard; this is the main component's run-button and metadata logic they left.
Pure EXTEND of menu.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
MenuComponent (frontend/src/app/workspace/component/menu/menu.component.ts, codecov 86.27 % — EXTEND menu.component.spec.ts)
getRunButtonBehavior (392–456) — a switch (this.executionState) returning a different button descriptor per state. Set executionState to each ExecutionState value, call the method, and assert the returned { text, icon, onClick, disabled } — in particular that the onClick wired for the run-eligible states invokes runWorkflow (392, 407, 456) and that the paused/running/completed/failed states return their distinct descriptors (410, 431, 438, 445, 452). Assert the effect of calling the returned onClick, not its source text.
ngOnInit run-button wiring (222–241) — the execution-state subscription sets executionState and calls applyRunButtonBehavior(getRunButtonBehavior()); emit a state event and assert the button updates. Line 241 also sets isExportDeactivate from !config.env.exportExecutionResultEnabled || !hasResults — cover the flag on/off and results present/absent so both sides of the || are taken.
onClickGenerateReport (506–525) — the report assembly and its two failure branches: the operator-results retrieval error (516, 518) and the catch that reports (e as Error).message (523, 525). Stub the results service to succeed, to error on retrieval, and to throw, and assert notificationService.error / remove are called accordingly.
onClickEditDescription (614–643) — building the updatedWorkflow with description ?? "", calling setWorkflowMetadata, and the if (this.userService.isLogin()) branch that persists then closes the modal. Cover logged-in and logged-out.
persistWorkflow (663–668) — the filter(isDefined(localPid) && isDefined(workflow.wid)) gate feeding addWorkflowToProject, and the error handler. Cover a workflow with and without a project id/wid, and the error path.
onWorkflowNameChange (678) — the if (this.userService.isLogin()) guard.
registerWorkflowMetadataDisplayRefresh (694–700) and handleWorkflowVersionDisplay (722) — the metadata-refresh subscription that sets currentWorkflowName, schedules adjustWorkflowNameWidth, and derives autoSaveState from lastModifiedTime === undefined / creationTime === undefined. Emit metadata with and without those timestamps.
runWorkflow (794–805) — the currentWorkflowName-derived default name and the currentExecutionName || "Untitled Execution" fallback.
Determinism notes:
registerWorkflowMetadataDisplayRefresh calls setTimeout(() => this.adjustWorkflowNameWidth(), 0). Do not add vi.useFakeTimers() merely to stop it — a synchronous test body cannot let it fire, and fake timers over zone.js's patched setTimeout have caused Node-version-dependent failures. If you need to assert the width adjustment, call adjustWorkflowNameWidth directly.
- Stub
userService, notificationService, workflowActionService, userProjectService and the results service; emit with of(...) / throwError(...) so streams resolve synchronously — never a real backend.
fixture.detectChanges() after each state change and fixture.destroy() in afterEach; vi.restoreAllMocks() in afterEach.
- Do not assert on
DatePipe-rendered text or on measured geometry (adjustWorkflowNameWidth reads element widths, which are zero under jsdom — assert it was called, not the width it computed).
Note open PRs touch this component (#5774, and the toolbar entries) — rebase before finishing.
Task Type
Task Summary
MenuComponent's class file is at 86.27 % with 26 uncovered lines, concentrated ingetRunButtonBehavior— the method that maps each execution state to the run/pause/resume button's label, icon and click handler. Two earlier passes (#7352, #7775) covered the operator-menu sub-component and the clipboard; this is the main component's run-button and metadata logic they left.Pure EXTEND of
menu.component.spec.ts. Vitest/jsdom; seefrontend/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
MenuComponent(frontend/src/app/workspace/component/menu/menu.component.ts, codecov 86.27 % — EXTENDmenu.component.spec.ts)getRunButtonBehavior(392–456) — aswitch (this.executionState)returning a different button descriptor per state. SetexecutionStateto eachExecutionStatevalue, call the method, and assert the returned{ text, icon, onClick, disabled }— in particular that theonClickwired for the run-eligible states invokesrunWorkflow(392, 407, 456) and that the paused/running/completed/failed states return their distinct descriptors (410, 431, 438, 445, 452). Assert the effect of calling the returnedonClick, not its source text.ngOnInitrun-button wiring (222–241) — the execution-state subscription setsexecutionStateand callsapplyRunButtonBehavior(getRunButtonBehavior()); emit a state event and assert the button updates. Line 241 also setsisExportDeactivatefrom!config.env.exportExecutionResultEnabled || !hasResults— cover the flag on/off and results present/absent so both sides of the||are taken.onClickGenerateReport(506–525) — the report assembly and its two failure branches: the operator-results retrieval error (516, 518) and thecatchthat reports(e as Error).message(523, 525). Stub the results service to succeed, to error on retrieval, and to throw, and assertnotificationService.error/removeare called accordingly.onClickEditDescription(614–643) — building theupdatedWorkflowwithdescription ?? "", callingsetWorkflowMetadata, and theif (this.userService.isLogin())branch that persists then closes the modal. Cover logged-in and logged-out.persistWorkflow(663–668) — thefilter(isDefined(localPid) && isDefined(workflow.wid))gate feedingaddWorkflowToProject, and the error handler. Cover a workflow with and without a project id/wid, and the error path.onWorkflowNameChange(678) — theif (this.userService.isLogin())guard.registerWorkflowMetadataDisplayRefresh(694–700) andhandleWorkflowVersionDisplay(722) — the metadata-refresh subscription that setscurrentWorkflowName, schedulesadjustWorkflowNameWidth, and derivesautoSaveStatefromlastModifiedTime === undefined/creationTime === undefined. Emit metadata with and without those timestamps.runWorkflow(794–805) — thecurrentWorkflowName-derived default name and thecurrentExecutionName || "Untitled Execution"fallback.Determinism notes:
registerWorkflowMetadataDisplayRefreshcallssetTimeout(() => this.adjustWorkflowNameWidth(), 0). Do not addvi.useFakeTimers()merely to stop it — a synchronous test body cannot let it fire, and fake timers over zone.js's patchedsetTimeouthave caused Node-version-dependent failures. If you need to assert the width adjustment, calladjustWorkflowNameWidthdirectly.userService,notificationService,workflowActionService,userProjectServiceand the results service; emit withof(...)/throwError(...)so streams resolve synchronously — never a real backend.fixture.detectChanges()after each state change andfixture.destroy()inafterEach;vi.restoreAllMocks()inafterEach.DatePipe-rendered text or on measured geometry (adjustWorkflowNameWidthreads element widths, which are zero under jsdom — assert it was called, not the width it computed).Note open PRs touch this component (#5774, and the toolbar entries) — rebase before finishing.
Task Type