Skip to content

Commit 594f545

Browse files
xuang7claude
andcommitted
test(ui): cover user-dataset template via instrumented render
Render the real (instrumented) UserDatasetComponent template, overriding only the heavy child components to empty shells, so the view-toggle buttons and the #datasetCardTpl markup are exercised without dragging in the children's ng-zorro internals. Takes user-dataset.component.html line coverage from 0% to 100%. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 871cb72 commit 594f545

1 file changed

Lines changed: 122 additions & 0 deletions

File tree

frontend/src/app/dashboard/component/user/user-dataset/user-dataset.component.spec.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,32 @@ import { USER_DATASET } from "../../../../app-routing.constant";
2323
import { UserDatasetVersionCreatorComponent } from "./user-dataset-explorer/user-dataset-version-creator/user-dataset-version-creator.component";
2424
import { SortMethod } from "../../../type/sort-method";
2525
import { User } from "../../../../common/type/user";
26+
import { ComponentFixture, TestBed } from "@angular/core/testing";
27+
import { By } from "@angular/platform-browser";
28+
import { NgIf, NgTemplateOutlet } from "@angular/common";
29+
import { Router } from "@angular/router";
30+
import { RouterTestingModule } from "@angular/router/testing";
31+
import { HttpClientTestingModule } from "@angular/common/http/testing";
32+
import { NoopAnimationsModule } from "@angular/platform-browser/animations";
33+
import { NzModalService } from "ng-zorro-antd/modal";
34+
import { NzMessageService } from "ng-zorro-antd/message";
35+
import { en_US, NZ_I18N } from "ng-zorro-antd/i18n";
36+
import { UserService } from "../../../../common/service/user/user.service";
37+
import { StubUserService } from "../../../../common/service/user/stub-user.service";
38+
import { SearchService } from "../../../service/user/search.service";
39+
import { DatasetService } from "../../../service/user/dataset/dataset.service";
40+
import { WorkflowPersistService } from "../../../../common/service/workflow-persist/workflow-persist.service";
41+
import { StubWorkflowPersistService } from "../../../../common/service/workflow-persist/stub-workflow-persist.service";
42+
import { UserProjectService } from "../../../service/user/project/user-project.service";
43+
import { StubUserProjectService } from "../../../service/user/project/stub-user-project.service";
44+
import { OperatorMetadataService } from "../../../../workspace/service/operator-metadata/operator-metadata.service";
45+
import { StubOperatorMetadataService } from "../../../../workspace/service/operator-metadata/stub-operator-metadata.service";
46+
import { FiltersComponent } from "../filters/filters.component";
47+
import { FiltersInstructionsComponent } from "../filters-instructions/filters-instructions.component";
48+
import { SearchResultsComponent } from "../search-results/search-results.component";
49+
import { CardItemComponent } from "../list-item/card-item/card-item.component";
50+
import { DashboardEntry } from "../../../type/dashboard-entry";
51+
import { commonTestProviders } from "../../../../common/testing/test-utils";
2652

2753
type LoadMoreFn = (start: number, count: number) => Promise<{ entries: any[]; more: boolean }>;
2854

@@ -361,3 +387,99 @@ describe("UserDatasetComponent", () => {
361387
});
362388
});
363389
});
390+
391+
// Renders the real (instrumented) UserDatasetComponent template so its view-toggle
392+
// buttons and `#datasetCardTpl` markup are exercised for coverage. We deliberately do
393+
// NOT override UserDatasetComponent itself (that would JIT-recompile it without
394+
// instrumentation); instead each heavy child component is stripped to an empty shell so
395+
// the test does not drag in their ng-zorro internals (menus, date pickers, locale data).
396+
describe("UserDatasetComponent template", () => {
397+
const VIEW_MODE_KEY = "texera.userDataset.viewMode";
398+
let fixture: ComponentFixture<UserDatasetComponent>;
399+
let component: UserDatasetComponent;
400+
401+
const makeDatasetEntry = (overrides: Partial<DashboardEntry> = {}): DashboardEntry =>
402+
({
403+
id: 5,
404+
name: "ds",
405+
description: "",
406+
type: "dataset",
407+
dataset: { isOwner: true },
408+
accessibleUserIds: [],
409+
likeCount: 0,
410+
viewCount: 0,
411+
isLiked: false,
412+
size: 0,
413+
...overrides,
414+
}) as unknown as DashboardEntry;
415+
416+
beforeEach(async () => {
417+
localStorage.removeItem(VIEW_MODE_KEY); // viewType then defaults to "card"
418+
419+
await TestBed.configureTestingModule({
420+
imports: [UserDatasetComponent, HttpClientTestingModule, NoopAnimationsModule, RouterTestingModule],
421+
providers: [
422+
NzModalService,
423+
{ provide: NZ_I18N, useValue: en_US },
424+
{ provide: UserService, useClass: StubUserService },
425+
{ provide: WorkflowPersistService, useValue: new StubWorkflowPersistService([]) },
426+
{ provide: UserProjectService, useClass: StubUserProjectService },
427+
{ provide: OperatorMetadataService, useClass: StubOperatorMetadataService },
428+
{ provide: Router, useValue: { navigate: vi.fn() } },
429+
{ provide: SearchService, useValue: { executeSearch: vi.fn(() => of({ entries: [], more: false })) } },
430+
{
431+
provide: DatasetService,
432+
useValue: { getDatasetCoverUrl: vi.fn(() => of({ url: null })), deleteDatasets: vi.fn(() => of({})) },
433+
},
434+
{ provide: NzMessageService, useValue: { warning: vi.fn() } },
435+
...commonTestProviders,
436+
],
437+
})
438+
.overrideComponent(FiltersComponent, { set: { template: "", imports: [] } })
439+
.overrideComponent(FiltersInstructionsComponent, { set: { template: "", imports: [] } })
440+
.overrideComponent(CardItemComponent, { set: { template: "", imports: [] } })
441+
.overrideComponent(SearchResultsComponent, {
442+
set: {
443+
// Render the parent-supplied card template for the first entry so the
444+
// `#datasetCardTpl` body (which lives in the instrumented parent template) runs.
445+
template:
446+
'<ng-container *ngIf="viewMode === \'card\' && cardTemplate && entries.length" [ngTemplateOutlet]="cardTemplate" [ngTemplateOutletContext]="{ $implicit: entries[0] }"></ng-container>',
447+
imports: [NgIf, NgTemplateOutlet],
448+
},
449+
})
450+
.compileComponents();
451+
452+
fixture = TestBed.createComponent(UserDatasetComponent);
453+
component = fixture.componentInstance;
454+
fixture.detectChanges();
455+
});
456+
457+
afterEach(() => localStorage.removeItem(VIEW_MODE_KEY));
458+
459+
const buttonByTitle = (title: string): HTMLButtonElement =>
460+
Array.from(fixture.nativeElement.querySelectorAll("button")).find(
461+
(b: any) => b.getAttribute("title") === title
462+
) as HTMLButtonElement;
463+
464+
it("renders both view-mode toggles and switches viewType on click", () => {
465+
expect(component.viewType).toBe("card");
466+
expect(buttonByTitle("List View")).toBeTruthy();
467+
expect(buttonByTitle("Card View")).toBeTruthy();
468+
469+
buttonByTitle("List View").click();
470+
fixture.detectChanges();
471+
expect(component.viewType).toBe("list");
472+
473+
buttonByTitle("Card View").click();
474+
fixture.detectChanges();
475+
expect(component.viewType).toBe("card");
476+
});
477+
478+
it("renders the dataset card template for each entry in card view", () => {
479+
component.searchResultsComponent.entries = [makeDatasetEntry()];
480+
fixture.detectChanges();
481+
482+
const cards = fixture.debugElement.queryAll(By.css("texera-card-item"));
483+
expect(cards.length).toBe(1);
484+
});
485+
});

0 commit comments

Comments
 (0)