This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git


The following commit(s) were added to refs/heads/main by this push:
     new 96ed553760 test(frontend): render DatasetDetailComponent template 
branches for coverage (#7419)
96ed553760 is described below

commit 96ed553760a299f2031f4cd83c024e9327cb5346
Author: Meng Wang <[email protected]>
AuthorDate: Sat Aug 8 04:13:59 2026 -0700

    test(frontend): render DatasetDetailComponent template branches for 
coverage (#7419)
    
    ### What changes were proposed in this PR?
    
    Extends the existing `DatasetDetailComponent` spec so the detail view's
    markup
    actually renders, covering template branches that were previously never
    executed
    
    
(`frontend/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.html`).
    The spec previously drove the class directly and never queried the DOM.
    No
    production code was changed.
    
    9 tests drive the template through the DOM:
    
    - **Like tag** — likes when logged in, unlikes when already liked, and
    stays
      inert (with the `disabled` class) when logged out, exercising the
      `(click)="isLogin && toggleLike()"` guard.
    - **Cover image** — the `*ngIf` omits the `<img>` without a cover URL
    and the
      `[src]` binding renders it when one is present.
    - **Right bar** — both arms of the collapse/restore `*ngIf` pair are
    clicked.
    - **Settings tab** — the dataset-name `[(ngModel)]` input renders and
    its Save
    button routes to the service; both `nz-switch` toggles are present and
    their
    change handlers reach `updateDatasetPublicity` /
    `updateDatasetDownloadable`.
    - **Contributors** — the `*ngFor` renders the seeded contributor rows.
    
    Three component behaviours the tests had to account for, noted in
    comments so the
    setup isn't mistaken for boilerplate:
    
    - `toggleLike()` early-returns unless `currentUid` is set — the spec's
    existing
    `login()` helper supplies it (the stub user service emits before the
    component
      subscribes).
    - `ngOnInit`'s subscriptions reset fields such as `coverImageUrl`, so
    the helper
    runs one change-detection pass first, then applies the test state, then
    renders.
    - `nz-tabs` only renders the active tab, and the Settings tab is
    additionally
    behind `*ngIf="userHasWriteAccess()"`, so an `openTab()` helper switches
    tabs
      and the access level is seeded.
    
    Per the issue's determinism constraints: no fake timers are introduced,
    no
    date/time string is asserted, and no layout or geometry is asserted.
    
    ### Any related issues, documentation, discussions?
    
    Closes #7409
    
    ### How was this PR tested?
    
    Extended 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-dataset/user-dataset-explorer/dataset-detail.component.spec.ts
    # Test Files 1 passed (1) | Tests 104 passed (104)
    prettier --write <spec>   # clean
    eslint  <spec>            # clean
    ```
    
    ### Was this PR authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Code (Opus 4.8 [1M context])
---
 .../dataset-detail.component.spec.ts               | 155 +++++++++++++++++++++
 1 file changed, 155 insertions(+)

diff --git 
a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.spec.ts
 
b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.spec.ts
index 43d9481167..63223d125f 100644
--- 
a/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.spec.ts
+++ 
b/frontend/src/app/dashboard/component/user/user-dataset/user-dataset-explorer/dataset-detail.component.spec.ts
@@ -18,6 +18,7 @@
  */
 
 import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { By } from "@angular/platform-browser";
 import { ActivatedRoute, Router } from "@angular/router";
 import { of, Subject, throwError } from "rxjs";
 import { NzModalService } from "ng-zorro-antd/modal";
@@ -1673,4 +1674,158 @@ describe("DatasetDetailComponent behavior", () => {
       
expect(datasetServiceStub.updateDatasetContributors).not.toHaveBeenCalled();
     });
   });
+
+  // ─── template rendering 
────────────────────────────────────────────────────
+  // These drive the markup through the DOM (rather than calling handlers 
directly)
+  // so the template's bindings and conditional blocks actually execute.
+  describe("template rendering", () => {
+    // Renders the component and applies the given state, so each *ngIf arm is 
exercised.
+    // The first detectChanges() lets ngOnInit's subscriptions settle — they 
reset fields
+    // such as coverImageUrl — so the state is applied afterwards and rendered 
by a
+    // second change-detection pass.
+    const renderWith = (state: Partial<DatasetDetailComponent> = {}): void => {
+      createComponent();
+      fixture.detectChanges();
+      Object.assign(component, state);
+      fixture.detectChanges();
+    };
+
+    const clickByCss = (selector: string): void => {
+      const el = fixture.debugElement.query(By.css(selector));
+      expect(el).toBeTruthy();
+      el.triggerEventHandler("click", null);
+      fixture.detectChanges();
+    };
+
+    // nz-tabs renders only the active tab's content, so a tab must be opened 
by its
+    // title before the markup inside it can be queried.
+    const openTab = (title: string): void => {
+      const tab = fixture.debugElement
+        .queryAll(By.css(".ant-tabs-tab"))
+        .find(el => (el.nativeElement.textContent ?? "").includes(title));
+      expect(tab).toBeTruthy();
+      tab!.nativeElement.click();
+      fixture.detectChanges();
+    };
+
+    it("toggles the like through the like tag when logged in", () => {
+      // toggleLike() early-returns unless currentUid is set, which login() 
supplies
+      createComponent();
+      fixture.detectChanges();
+      login();
+      Object.assign(component, { isLogin: true, did: 5, isLiked: false, 
likeCount: 1 });
+      fixture.detectChanges();
+
+      clickByCss(".like-tag");
+
+      expect(hubServiceStub.postLike).toHaveBeenCalled();
+    });
+
+    it("unlikes through the same tag when the dataset is already liked", () => 
{
+      createComponent();
+      fixture.detectChanges();
+      login();
+      Object.assign(component, { isLogin: true, did: 5, isLiked: true, 
likeCount: 2 });
+      fixture.detectChanges();
+
+      clickByCss(".like-tag");
+
+      expect(hubServiceStub.postUnlike).toHaveBeenCalled();
+    });
+
+    it("does not toggle the like when logged out", () => {
+      renderWith({ isLogin: false, did: 5, isLiked: false, likeCount: 1 });
+
+      const likeTag = fixture.debugElement.query(By.css(".like-tag"));
+      expect(likeTag).toBeTruthy();
+      // the template guards the handler with `isLogin &&`
+      expect(likeTag.nativeElement.classList).toContain("disabled");
+
+      likeTag.triggerEventHandler("click", null);
+
+      expect(hubServiceStub.postLike).not.toHaveBeenCalled();
+    });
+
+    it("omits the cover image when there is no cover URL", () => {
+      renderWith({ coverImageUrl: null });
+      
expect(fixture.debugElement.query(By.css(".dataset-cover-image"))).toBeNull();
+    });
+
+    it("renders the cover image bound to the cover URL", () => {
+      renderWith({ coverImageUrl: "blob:cover" });
+      const img = fixture.debugElement.query(By.css(".dataset-cover-image"));
+      expect(img).toBeTruthy();
+      expect(img.nativeElement.getAttribute("src")).toBe("blob:cover");
+    });
+
+    it("collapses the right bar from the template, then renders the restore 
control", () => {
+      renderWith({ isRightBarCollapsed: false });
+      openTab("Versions & Files");
+
+      // both arms of the *ngIf pair are exercised: hide first, then the show 
button
+      clickByCss("button[nz-tooltip='Hide the right bar']");
+      expect(component.isRightBarCollapsed).toBe(true);
+
+      clickByCss("button[nz-tooltip='Show Tree']");
+      expect(component.isRightBarCollapsed).toBe(false);
+    });
+
+    it("binds the dataset name input and saves it from the template", () => {
+      // the Settings tab is behind *ngIf="userHasWriteAccess()"
+      renderWith({ did: 5, editedDatasetName: "renamed", 
userDatasetAccessLevel: "WRITE" });
+      openTab("Settings");
+
+      const input = fixture.debugElement.query(By.css(".settings-name-controls 
input[nz-input]"));
+      expect(input).toBeTruthy();
+
+      // drive the [(ngModel)] update path through the DOM
+      input.nativeElement.value = "typed-name";
+      input.nativeElement.dispatchEvent(new Event("input"));
+      fixture.detectChanges();
+      expect(component.editedDatasetName).toBe("typed-name");
+
+      const saveBtn = fixture.debugElement
+        .queryAll(By.css("button"))
+        .find(btn => (btn.nativeElement.textContent ?? "").trim() === "Save");
+      expect(saveBtn).toBeTruthy();
+      saveBtn!.triggerEventHandler("click", null);
+
+      expect(datasetServiceStub.updateDatasetName).toHaveBeenCalledWith(5, 
"typed-name");
+    });
+
+    it("renders every contributor row from the list", () => {
+      renderWith({
+        did: 5,
+        datasetContributors: [
+          { name: "Ada", email: "[email protected]", affiliation: "" } as Contributor,
+          { name: "Grace", email: "[email protected]", affiliation: "" } as 
Contributor,
+        ],
+      });
+
+      const rendered = fixture.debugElement.nativeElement.textContent ?? "";
+      expect(rendered).toContain("Ada");
+      expect(rendered).toContain("Grace");
+    });
+
+    it("routes the settings switches' ngModelChange bindings to the service", 
() => {
+      renderWith({
+        did: 5,
+        datasetIsPublic: false,
+        datasetIsDownloadable: true,
+        userDatasetAccessLevel: "WRITE",
+        isOwner: true, // the downloadable switch is [nzDisabled]="!isOwner"
+      });
+      openTab("Settings");
+
+      const switches = fixture.debugElement.queryAll(By.css("nz-switch"));
+      expect(switches.length).toBeGreaterThanOrEqual(2);
+
+      // fire the template's (ngModelChange) handlers rather than calling the 
methods
+      switches[0].triggerEventHandler("ngModelChange", true);
+      
expect(datasetServiceStub.updateDatasetPublicity).toHaveBeenCalledWith(5);
+
+      switches[1].triggerEventHandler("ngModelChange", false);
+      
expect(datasetServiceStub.updateDatasetDownloadable).toHaveBeenCalledWith(5);
+    });
+  });
 });

Reply via email to