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 aa3a117986 test: cover hub.component sidebar gating and routerLinks 
(#5232)
aa3a117986 is described below

commit aa3a1179864d5271773917a0baf36df95ed33ef4
Author: Matthew B. <[email protected]>
AuthorDate: Tue Jun 2 16:38:15 2026 -0700

    test: cover hub.component sidebar gating and routerLinks (#5232)
    
    ### What changes were proposed in this PR?
    - Replace the hub.component.spec.ts smoke test with nine tests covering
    default-input render, GuiConfigService injection, per-flag
    `sidebarTabs.*_enabled` gating, all-enabled rendering, exclusion of
    disabled tabs, `routerLink` bindings to the three routing constants, and
    `isLogin` input passthrough.
    - Wrap `HubComponent` in a test host `<ul nz-menu>` so `nz-menu-item`
    directives resolve their DI tokens the same way they do under the
    dashboard at runtime.
    - Read `routerLink` via the directive's `routerLinkInput` signal since
    the `routerLink` input is a write-only setter and
    `ng-reflect-router-link` is not populated in the Vitest environment.
    ### Any related issues, documentation, or discussions?
    Closes: #5224
    ### How was this PR tested?
    - `yarn test --include='src/app/hub/component/hub.component.spec.ts'`: 9
    passed, 0 failed.
    - `yarn format:fix`: 506 files unchanged.
    ### Was this PR authored or co-authored using generative AI tooling?
    Co-authored with Claude Opus 4.7 in compliance with ASF
    
    ---------
    
    Co-authored-by: Meng Wang <[email protected]>
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
 .../src/app/hub/component/hub.component.spec.ts    | 165 +++++++++++++++++++++
 1 file changed, 165 insertions(+)

diff --git a/frontend/src/app/hub/component/hub.component.spec.ts 
b/frontend/src/app/hub/component/hub.component.spec.ts
new file mode 100644
index 0000000000..5a88360639
--- /dev/null
+++ b/frontend/src/app/hub/component/hub.component.spec.ts
@@ -0,0 +1,165 @@
+/**
+ * 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 { Component } from "@angular/core";
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { By } from "@angular/platform-browser";
+import { HttpClientTestingModule } from "@angular/common/http/testing";
+import { NoopAnimationsModule } from "@angular/platform-browser/animations";
+import { RouterTestingModule } from "@angular/router/testing";
+import { RouterLink } from "@angular/router";
+import { NzMenuModule } from "ng-zorro-antd/menu";
+
+import { HubComponent } from "./hub.component";
+import { commonTestProviders } from "../../common/testing/test-utils";
+import { GuiConfigService } from "../../common/service/gui-config.service";
+import { SidebarTabs } from "../../common/type/gui-config";
+import { HOME, HUB_DATASET_RESULT, HUB_WORKFLOW_RESULT } from 
"../../app-routing.constant";
+
+// Full SidebarTabs with all flags off; tests enable only the ones they need.
+function makeSidebarTabs(overrides: Partial<SidebarTabs> = {}): SidebarTabs {
+  return {
+    hub_enabled: false,
+    home_enabled: false,
+    workflow_enabled: false,
+    dataset_enabled: false,
+    your_work_enabled: false,
+    projects_enabled: false,
+    workflows_enabled: false,
+    compute_enabled: false,
+    datasets_enabled: false,
+    quota_enabled: false,
+    forum_enabled: false,
+    about_enabled: false,
+    ...overrides,
+  };
+}
+
+// Host wraps HubComponent in `<ul nz-menu>` so nz-menu-item directives 
resolve their DI tokens.
+@Component({
+  template: `<ul nz-menu>
+    <texera-hub
+      [isLogin]="isLogin"
+      [sidebarTabs]="sidebarTabs"></texera-hub>
+  </ul>`,
+  imports: [HubComponent, NzMenuModule],
+})
+class TestHostComponent {
+  isLogin = false;
+  sidebarTabs: SidebarTabs = makeSidebarTabs();
+}
+
+describe("HubComponent", () => {
+  let hostFixture: ComponentFixture<TestHostComponent>;
+  let host: TestHostComponent;
+
+  function setup(isLogin: boolean, sidebarTabs: SidebarTabs): HubComponent {
+    TestBed.configureTestingModule({
+      imports: [TestHostComponent, HttpClientTestingModule, 
NoopAnimationsModule, RouterTestingModule.withRoutes([])],
+      providers: [...commonTestProviders],
+    });
+    hostFixture = TestBed.createComponent(TestHostComponent);
+    host = hostFixture.componentInstance;
+    host.isLogin = isLogin;
+    host.sidebarTabs = sidebarTabs;
+    hostFixture.detectChanges();
+    return 
hostFixture.debugElement.query(By.directive(HubComponent)).componentInstance as 
HubComponent;
+  }
+
+  // Text of every rendered menu item.
+  function renderedMenuLabels(): string[] {
+    return hostFixture.debugElement
+      .queryAll(By.css("[nz-menu-item]"))
+      .map(de => (de.nativeElement.textContent ?? "").trim());
+  }
+
+  // RouterLink target of the menu item containing `label`.
+  function routerLinkFor(label: string): string {
+    const item = hostFixture.debugElement
+      .queryAll(By.css("[nz-menu-item]"))
+      .find(de => (de.nativeElement.textContent ?? "").includes(label));
+    expect(item).toBeTruthy();
+    // `routerLink` is a write-only setter; read the resolved value off the 
routerLinkInput signal.
+    const link = item!.injector.get(RouterLink) as unknown as { 
routerLinkInput: () => string | string[] };
+    return ([] as string[]).concat(link.routerLinkInput()).join("");
+  }
+
+  it("creates with default isLogin = false and an empty sidebarTabs (no menu 
items render)", () => {
+    const component = setup(false, makeSidebarTabs());
+    expect(component).toBeTruthy();
+    expect(component.isLogin).toBe(false);
+    expect(renderedMenuLabels().length).toBe(0);
+  });
+
+  it("passes the isLogin input through to the component", () => {
+    const component = setup(true, makeSidebarTabs());
+    expect(component.isLogin).toBe(true);
+  });
+
+  it("injects GuiConfigService and exposes it for template gating", () => {
+    const component = setup(false, makeSidebarTabs());
+    expect((component as unknown as { config: GuiConfigService 
}).config).toBe(TestBed.inject(GuiConfigService));
+  });
+
+  it("renders only the Home item when home_enabled is the only flag set", () 
=> {
+    setup(false, makeSidebarTabs({ home_enabled: true }));
+    const labels = renderedMenuLabels();
+    expect(labels.length).toBe(1);
+    expect(labels[0]).toContain("Home");
+  });
+
+  it("renders only the Workflows item when workflow_enabled is the only flag 
set", () => {
+    setup(false, makeSidebarTabs({ workflow_enabled: true }));
+    const labels = renderedMenuLabels();
+    expect(labels.length).toBe(1);
+    expect(labels[0]).toContain("Workflows");
+  });
+
+  it("renders only the Datasets item when dataset_enabled is the only flag 
set", () => {
+    setup(false, makeSidebarTabs({ dataset_enabled: true }));
+    const labels = renderedMenuLabels();
+    expect(labels.length).toBe(1);
+    expect(labels[0]).toContain("Datasets");
+  });
+
+  it("renders all three menu items when home, workflow, and dataset flags are 
enabled", () => {
+    setup(false, makeSidebarTabs({ home_enabled: true, workflow_enabled: true, 
dataset_enabled: true }));
+    const labels = renderedMenuLabels();
+    expect(labels.length).toBe(3);
+    expect(labels.some(l => l.includes("Home"))).toBe(true);
+    expect(labels.some(l => l.includes("Workflows"))).toBe(true);
+    expect(labels.some(l => l.includes("Datasets"))).toBe(true);
+  });
+
+  it("excludes disabled tabs while rendering enabled ones", () => {
+    setup(false, makeSidebarTabs({ home_enabled: true, workflow_enabled: 
false, dataset_enabled: true }));
+    const labels = renderedMenuLabels();
+    expect(labels.length).toBe(2);
+    expect(labels.some(l => l.includes("Home"))).toBe(true);
+    expect(labels.some(l => l.includes("Datasets"))).toBe(true);
+    expect(labels.some(l => l.includes("Workflows"))).toBe(false);
+  });
+
+  it("binds each menu item's routerLink to the correct routing constant", () 
=> {
+    setup(false, makeSidebarTabs({ home_enabled: true, workflow_enabled: true, 
dataset_enabled: true }));
+    expect(routerLinkFor("Home")).toBe(HOME);
+    expect(routerLinkFor("Workflows")).toBe(HUB_WORKFLOW_RESULT);
+    expect(routerLinkFor("Datasets")).toBe(HUB_DATASET_RESULT);
+  });
+});

Reply via email to