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-6575-8937f71c616ae2beb3606a43c8c38fe907380987
in repository https://gitbox.apache.org/repos/asf/texera.git

commit 99a8815c10f1cbc5cda40fa210171b6052b63c79
Author: Xinyuan Lin <[email protected]>
AuthorDate: Sun Jul 19 23:29:42 2026 -0700

    test(frontend): add unit tests for PublicProjectComponent (#6575)
    
    ### What changes were proposed in this PR?
    
    Adds a Vitest unit spec for `PublicProjectComponent`, which previously
    had no test coverage. The 10 tests cover:
    - `ngOnInit` loading `publicProjectEntries` and rendering table rows.
    - reading `disabledList` from `NZ_MODAL_DATA`.
    - `updateCheckedSet` add/remove and `onItemChecked`.
    - `onAllChecked(true/false)` checking/unchecking every entry.
    - `refreshCheckedStatus` for all-, none-, and some-selected
    (`checked`/`indeterminate`).
    - `addPublicProjects` calling the service with `Array.from(checkedList)`
    and destroying the modal on success.
    
    The component injects `NzModalRef` and `NZ_MODAL_DATA`, both provided in
    the TestBed.
    
    ### Any related issues, documentation, discussions?
    
    Closes #6572.
    
    ### How was this PR tested?
    
    `npx ng test --watch=false
    --include='**/public-project.component.spec.ts'` -> 10/10 passing. `yarn
    format:ci` passes.
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 4.8 [1M context])
    
    ---------
    
    Signed-off-by: Xinyuan Lin <[email protected]>
    Co-authored-by: Copilot Autofix powered by AI 
<[email protected]>
---
 .../public-project.component.spec.ts               | 187 +++++++++++++++++++++
 1 file changed, 187 insertions(+)

diff --git 
a/frontend/src/app/dashboard/component/user/user-project/public-project/public-project.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-project/public-project/public-project.component.spec.ts
new file mode 100644
index 0000000000..452e22ab01
--- /dev/null
+++ 
b/frontend/src/app/dashboard/component/user/user-project/public-project/public-project.component.spec.ts
@@ -0,0 +1,187 @@
+/**
+ * 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 { NoopAnimationsModule } from "@angular/platform-browser/animations";
+import { of } from "rxjs";
+import { NZ_MODAL_DATA, NzModalRef } from "ng-zorro-antd/modal";
+import { PublicProjectComponent } from "./public-project.component";
+import { PublicProjectService } from 
"../../../../service/user/public-project/public-project.service";
+import { PublicProject } from "../../../../type/dashboard-project.interface";
+import { commonTestProviders } from "../../../../../common/testing/test-utils";
+
+describe("PublicProjectComponent", () => {
+  let fixture: ComponentFixture<PublicProjectComponent>;
+  let component: PublicProjectComponent;
+
+  const sampleProjects: PublicProject[] = [
+    { pid: 1, name: "Alpha", owner: "alice", creationTime: 1_000 },
+    { pid: 2, name: "Bravo", owner: "bob", creationTime: 2_000 },
+    { pid: 3, name: "Charlie", owner: "carol", creationTime: 3_000 },
+  ];
+
+  let mockPublicProjectService: {
+    getPublicProjects: ReturnType<typeof vi.fn>;
+    addPublicProjects: ReturnType<typeof vi.fn>;
+  };
+  let modalRef: { destroy: ReturnType<typeof vi.fn> };
+  let disabledList: Set<number>;
+
+  beforeEach(async () => {
+    mockPublicProjectService = {
+      getPublicProjects: vi.fn().mockReturnValue(of(sampleProjects)),
+      addPublicProjects: vi.fn().mockReturnValue(of(undefined)),
+    };
+    modalRef = { destroy: vi.fn() };
+    // pid 2 is disabled to verify the component reads disabledList from 
NZ_MODAL_DATA.
+    disabledList = new Set<number>([2]);
+
+    await TestBed.configureTestingModule({
+      imports: [PublicProjectComponent, NoopAnimationsModule],
+      providers: [
+        ...commonTestProviders,
+        { provide: PublicProjectService, useValue: mockPublicProjectService },
+        { provide: NzModalRef, useValue: modalRef },
+        { provide: NZ_MODAL_DATA, useValue: { disabledList } },
+      ],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(PublicProjectComponent);
+    component = fixture.componentInstance;
+  });
+
+  afterEach(() => {
+    fixture?.destroy();
+    document.querySelectorAll(".cdk-overlay-container").forEach(c => 
(c.innerHTML = ""));
+  });
+
+  it("should create and read disabledList from NZ_MODAL_DATA", () => {
+    fixture.detectChanges();
+    expect(component).toBeTruthy();
+    expect(component.disabledList).toBe(disabledList);
+    expect(component.disabledList.has(2)).toBe(true);
+  });
+
+  it("ngOnInit loads publicProjectEntries from getPublicProjects and renders 
the rows", () => {
+    fixture.detectChanges();
+    
expect(mockPublicProjectService.getPublicProjects).toHaveBeenCalledTimes(1);
+    expect(component.publicProjectEntries).toEqual(sampleProjects);
+
+    const rows = (fixture.nativeElement as 
HTMLElement).querySelectorAll("tbody tr");
+    expect(rows.length).toBe(sampleProjects.length);
+    const text = (fixture.nativeElement as HTMLElement).textContent ?? "";
+    expect(text).toContain("Alpha");
+    expect(text).toContain("alice");
+    expect(text).toContain("Selected 0 items");
+  });
+
+  it("updateCheckedSet adds an id when checked and removes it when unchecked", 
() => {
+    fixture.detectChanges();
+
+    component.updateCheckedSet(1, true);
+    expect(component.checkedList.has(1)).toBe(true);
+
+    component.updateCheckedSet(1, false);
+    expect(component.checkedList.has(1)).toBe(false);
+  });
+
+  it("onItemChecked updates the set and refreshes the checked status", () => {
+    fixture.detectChanges();
+    const refreshSpy = vi.spyOn(component, "refreshCheckedStatus");
+
+    component.onItemChecked(1, true);
+
+    expect(component.checkedList.has(1)).toBe(true);
+    expect(refreshSpy).toHaveBeenCalledTimes(1);
+    // Only 1 of 3 selected => indeterminate, not fully checked.
+    expect(component.checked).toBe(false);
+    expect(component.indeterminate).toBe(true);
+
+    component.onItemChecked(1, false);
+    expect(component.checkedList.has(1)).toBe(false);
+    expect(refreshSpy).toHaveBeenCalledTimes(2);
+    expect(component.indeterminate).toBe(false);
+  });
+
+  it("onAllChecked(true) selects every entry", () => {
+    fixture.detectChanges();
+
+    component.onAllChecked(true);
+
+    sampleProjects.forEach(p => 
expect(component.checkedList.has(p.pid)).toBe(true));
+    expect(component.checkedList.size).toBe(sampleProjects.length);
+    expect(component.checked).toBe(true);
+    expect(component.indeterminate).toBe(false);
+  });
+
+  it("onAllChecked(false) unchecks all entries", () => {
+    fixture.detectChanges();
+    component.onAllChecked(true);
+
+    component.onAllChecked(false);
+
+    expect(component.checkedList.size).toBe(0);
+    expect(component.checked).toBe(false);
+    expect(component.indeterminate).toBe(false);
+  });
+
+  describe("refreshCheckedStatus", () => {
+    beforeEach(() => fixture.detectChanges());
+
+    it("all selected => checked true, indeterminate false", () => {
+      sampleProjects.forEach(p => component.checkedList.add(p.pid));
+
+      component.refreshCheckedStatus();
+
+      expect(component.checked).toBe(true);
+      expect(component.indeterminate).toBe(false);
+    });
+
+    it("none selected => checked false, indeterminate false", () => {
+      component.checkedList.clear();
+
+      component.refreshCheckedStatus();
+
+      expect(component.checked).toBe(false);
+      expect(component.indeterminate).toBe(false);
+    });
+
+    it("some selected => checked false, indeterminate true", () => {
+      component.checkedList.add(sampleProjects[0].pid);
+
+      component.refreshCheckedStatus();
+
+      expect(component.checked).toBe(false);
+      expect(component.indeterminate).toBe(true);
+    });
+  });
+
+  it("addPublicProjects calls the service with the checked ids and destroys 
the modal on success", () => {
+    fixture.detectChanges();
+    component.checkedList = new Set<number>([1, 3]);
+
+    component.addPublicProjects();
+
+    
expect(mockPublicProjectService.addPublicProjects).toHaveBeenCalledTimes(1);
+    const [[checkedIds]] = 
mockPublicProjectService.addPublicProjects.mock.calls;
+    expect(checkedIds).toHaveLength(2);
+    expect(checkedIds).toEqual(expect.arrayContaining([1, 3]));
+    expect(modalRef.destroy).toHaveBeenCalledTimes(1);
+  });
+});

Reply via email to