This is an automated email from the ASF dual-hosted git repository. github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-6512-501bc137ffff94867cdf0a533fdff0041b4f3888 in repository https://gitbox.apache.org/repos/asf/texera.git
commit 29656a4eff96671126fa9a47ec3be068b25dae43 Author: Meng Wang <[email protected]> AuthorDate: Sat Jul 18 12:01:28 2026 -0700 test(frontend): add unit test coverage for NgbdModalAddProjectWorkflowComponent (#6512) ### What changes were proposed in this PR? Adds a Vitest spec for `NgbdModalAddProjectWorkflowComponent` (the "add workflow to project" modal), which previously had no spec (codecov ~7%). 4 tests, using `TestBed` + `detectChanges()`, with `UserProjectService` and `WorkflowPersistService` stubbed (`vi.fn()` returning `of(...)`) and `NZ_MODAL_DATA` provided so the modal receives its `projectId`: - **create + render** — the component builds, `retrieveWorkflowsOfProject` / `retrieveWorkflowsBySessionUser` are called, `unaddedWorkflows` is populated, and the template renders one table row per unadded workflow. - **`isAllChecked()`** — true only when `checkedWorkflows` is non-empty and every entry is checked (covers the all/partial/empty cases). - **`changeAll()`** — checks all when some are unchecked, clears all when every one is checked. - **`submitForm()`** — calls `addWorkflowToProject(projectId, wid)` for each checked workflow only (skips unchecked) and pushes it to the `addedWorkflows` cache. The service stubs are inline because the existing `StubUserProjectService` / `StubWorkflowPersistService` leave the methods this component uses unimplemented. No production code was changed. ### Any related issues, documentation, discussions? Closes #6507 ### How was this PR tested? New unit tests, run locally in `frontend/` (all green; the failure path was verified by breaking an assertion to confirm the suite goes red): ``` ng test --watch=false --include src/app/dashboard/component/user/user-project/user-project-section/ngbd-modal-add-project-workflow/ngbd-modal-add-project-workflow.component.spec.ts # Tests 4 passed (4) eslint <spec> # clean prettier --check # clean ``` ### Was this PR authored or co-authored using generative AI tooling? Generated-by: Claude Code (Opus 4.8 [1M context]) --- ...bd-modal-add-project-workflow.component.spec.ts | 114 +++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/frontend/src/app/dashboard/component/user/user-project/user-project-section/ngbd-modal-add-project-workflow/ngbd-modal-add-project-workflow.component.spec.ts b/frontend/src/app/dashboard/component/user/user-project/user-project-section/ngbd-modal-add-project-workflow/ngbd-modal-add-project-workflow.component.spec.ts new file mode 100644 index 0000000000..f5aed447bf --- /dev/null +++ b/frontend/src/app/dashboard/component/user/user-project/user-project-section/ngbd-modal-add-project-workflow/ngbd-modal-add-project-workflow.component.spec.ts @@ -0,0 +1,114 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { of } from "rxjs"; +import { NZ_MODAL_DATA } from "ng-zorro-antd/modal"; +import { NgbdModalAddProjectWorkflowComponent } from "./ngbd-modal-add-project-workflow.component"; +import { UserProjectService } from "../../../../../service/user/project/user-project.service"; +import { WorkflowPersistService } from "../../../../../../common/service/workflow-persist/workflow-persist.service"; +import { DashboardWorkflow } from "../../../../../type/dashboard-workflow.interface"; + +const PROJECT_ID = 42; + +function workflowEntry(wid: number, name: string): DashboardWorkflow { + return { + workflow: { wid, name, creationTime: 0, lastModifiedTime: 0 }, + } as unknown as DashboardWorkflow; +} + +const wf1 = workflowEntry(1, "alpha"); +const wf2 = workflowEntry(2, "beta"); + +describe("NgbdModalAddProjectWorkflowComponent", () => { + let component: NgbdModalAddProjectWorkflowComponent; + let fixture: ComponentFixture<NgbdModalAddProjectWorkflowComponent>; + let userProjectService: { + retrieveWorkflowsOfProject: ReturnType<typeof vi.fn>; + addWorkflowToProject: ReturnType<typeof vi.fn>; + }; + let workflowPersistService: { retrieveWorkflowsBySessionUser: ReturnType<typeof vi.fn> }; + + beforeEach(async () => { + userProjectService = { + // No workflows already in the project, so every session workflow is "unadded". + retrieveWorkflowsOfProject: vi.fn().mockReturnValue(of([])), + addWorkflowToProject: vi.fn().mockReturnValue(of({} as Response)), + }; + workflowPersistService = { + retrieveWorkflowsBySessionUser: vi.fn().mockReturnValue(of([wf1, wf2])), + }; + + await TestBed.configureTestingModule({ + imports: [NgbdModalAddProjectWorkflowComponent], + providers: [ + { provide: NZ_MODAL_DATA, useValue: { projectId: PROJECT_ID } }, + { provide: UserProjectService, useValue: userProjectService }, + { provide: WorkflowPersistService, useValue: workflowPersistService }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(NgbdModalAddProjectWorkflowComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates and renders one row per unadded workflow", () => { + expect(component).toBeTruthy(); + expect(userProjectService.retrieveWorkflowsOfProject).toHaveBeenCalledWith(PROJECT_ID); + expect(workflowPersistService.retrieveWorkflowsBySessionUser).toHaveBeenCalled(); + expect(component.unaddedWorkflows).toEqual([wf1, wf2]); + expect(component.checkedWorkflows).toEqual([false, false]); + + const rows = fixture.nativeElement.querySelectorAll("tbody tr"); + expect(rows.length).toBe(2); + }); + + it("isAllChecked is true only when every workflow is checked", () => { + expect(component.isAllChecked()).toBe(false); // freshly [false, false] + component.checkedWorkflows = [true, true]; + expect(component.isAllChecked()).toBe(true); + component.checkedWorkflows = [true, false]; + expect(component.isAllChecked()).toBe(false); + component.checkedWorkflows = []; + expect(component.isAllChecked()).toBe(false); // empty is not "all checked" + }); + + it("changeAll checks all when some are unchecked, and clears all when every one is checked", () => { + // after init: [false, false] -> not all checked -> check all + component.changeAll(); + expect(component.checkedWorkflows).toEqual([true, true]); + // now all checked -> clear all + component.changeAll(); + expect(component.checkedWorkflows).toEqual([false, false]); + }); + + it("submitForm adds only the checked workflows to the project and caches them", () => { + component.checkedWorkflows = [true, false]; // check only wf1 + + component.submitForm(); + + expect(userProjectService.addWorkflowToProject).toHaveBeenCalledTimes(1); + expect(userProjectService.addWorkflowToProject).toHaveBeenCalledWith(PROJECT_ID, 1); + + const cache = (component as unknown as { addedWorkflows: DashboardWorkflow[] }).addedWorkflows; + expect(cache).toContain(wf1); + expect(cache).not.toContain(wf2); + }); +});
