feat: add Jest configuration and tests for category content view component

This commit is contained in:
4gray committed 2026-04-04 20:41:59 +02:00
1 parent 02c773170e
commit eef18dea68
8 files changed
+280 -1

No files matched your search

@@ -0,0 +1,21 @@
export default {
displayName: 'portal-catalog-feature',
preset: '../../../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
coverageDirectory: '../../../../coverage/libs/portal/catalog/feature',
transform: {
'^.+\\.(ts|mjs|js|html)$': [
'jest-preset-angular',
{
tsconfig: '<rootDir>/tsconfig.spec.json',
stringifyContentPathRegex: '\\.(html|svg)$',
},
],
},
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
snapshotSerializers: [
'jest-preset-angular/build/serializers/no-ng-attributes',
'jest-preset-angular/build/serializers/ng-snapshot',
'jest-preset-angular/build/serializers/html-comment',
],
};
+8
View File
@@ -6,6 +6,14 @@
"projectType": "library",
"tags": ["scope:portal", "domain:portal-shared", "type:feature"],
"targets": {
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/portal/catalog/feature/jest.config.ts",
"tsConfig": "libs/portal/catalog/feature/tsconfig.spec.json"
}
},
"lint": {
"executor": "@nx/eslint:lint"
}
@@ -0,0 +1,175 @@
import { Component, input, output, signal } from '@angular/core';
import { NgComponentOutlet } from '@angular/common';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatIconButton } from '@angular/material/button';
import { MatIcon } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatTooltip } from '@angular/material/tooltip';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { of } from 'rxjs';
import {
PORTAL_CATALOG_DETAIL_COMPONENT,
PORTAL_CATALOG_FACADE,
PortalCatalogSortMode,
} from '@iptvnator/portal/shared/util';
import { CategoryContentViewComponent } from './category-content-view.component';
@Component({
selector: 'app-grid-list',
standalone: true,
template: '',
})
class MockGridListComponent {
readonly isLoading = input<boolean>();
readonly items = input<unknown[]>();
readonly pageIndex = input<number>();
readonly totalPages = input<number>();
readonly limit = input<number>();
readonly pageSizeOptions = input<number[]>();
readonly showPaginator = input(true);
readonly itemClicked = output<unknown>();
readonly pageChange = output<unknown>();
}
@Component({
selector: 'app-playlist-error-view',
standalone: true,
template: '',
})
class MockPlaylistErrorViewComponent {
readonly title = input('');
readonly description = input('');
readonly showActionButtons = input(true);
readonly viewType = input('');
}
@Component({
standalone: true,
template: '',
})
class MockDetailComponent {}
describe('CategoryContentViewComponent', () => {
let fixture: ComponentFixture<CategoryContentViewComponent>;
const isPaginatedContentLoading = signal(true);
const categoryItemCount = signal(0);
const contentSortMode = signal<PortalCatalogSortMode | null>(null);
const catalog = {
provider: 'xtream' as const,
pageSizeOptions: [10, 25, 50],
contentType: signal('vod'),
limit: signal(25),
pageIndex: signal(0),
selectedCategory: signal({ id: 1 }),
paginatedContent: signal<unknown[]>([]),
selectedCategoryTitle: signal('Movies'),
categoryItemCount,
selectedItem: signal(null),
totalPages: signal(0),
contentSortMode,
playlist: signal(null),
isPaginatedContentLoading,
initialize: jest.fn(),
clearSelectedItem: jest.fn(),
setPage: jest.fn(),
setLimit: jest.fn(),
setContentSortMode: jest.fn(),
selectItem: jest.fn().mockReturnValue(null),
getItemProgress: jest.fn().mockReturnValue({}),
};
beforeEach(async () => {
isPaginatedContentLoading.set(true);
categoryItemCount.set(0);
contentSortMode.set(null);
catalog.initialize.mockClear();
await TestBed.configureTestingModule({
imports: [CategoryContentViewComponent, NoopAnimationsModule],
providers: [
{
provide: TranslateService,
useValue: {
instant: (key: string) =>
key === 'WORKSPACE.SHELL.XTREAM_IMPORT_LOADING'
? 'Fetching playlist data from source...'
: key,
get: (key: string) =>
of(
key === 'WORKSPACE.SHELL.XTREAM_IMPORT_LOADING'
? 'Fetching playlist data from source...'
: key
),
stream: (key: string) =>
of(
key === 'WORKSPACE.SHELL.XTREAM_IMPORT_LOADING'
? 'Fetching playlist data from source...'
: key
),
onLangChange: of(null),
onTranslationChange: of(null),
onDefaultLangChange: of(null),
currentLang: 'en',
defaultLang: 'en',
},
},
{
provide: PORTAL_CATALOG_FACADE,
useValue: catalog,
},
{
provide: PORTAL_CATALOG_DETAIL_COMPONENT,
useValue: MockDetailComponent,
},
{
provide: ActivatedRoute,
useValue: {
snapshot: {
params: {},
},
},
},
{
provide: Router,
useValue: {
navigate: jest.fn(),
},
},
],
})
.overrideComponent(CategoryContentViewComponent, {
set: {
imports: [
NgComponentOutlet,
MockGridListComponent,
MockPlaylistErrorViewComponent,
MatIcon,
MatIconButton,
MatMenuModule,
MatPaginatorModule,
MatTooltip,
TranslatePipe,
],
},
})
.compileComponents();
fixture = TestBed.createComponent(CategoryContentViewComponent);
});
it('shows loading copy in the subtitle instead of 0 items while xtream content is still warming up', () => {
fixture.detectChanges();
const subtitle = fixture.nativeElement.querySelector(
'.category-subtitle'
) as HTMLElement | null;
expect(catalog.initialize).toHaveBeenCalledWith(null);
expect(subtitle?.textContent?.trim()).toBe(
'Fetching playlist data from source...'
);
});
});
@@ -6,7 +6,7 @@ import { MatMenuModule } from '@angular/material/menu';
import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
import { MatTooltip } from '@angular/material/tooltip';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslatePipe } from '@ngx-translate/core';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import {
GridListComponent,
PlaylistErrorViewComponent,
@@ -50,6 +50,7 @@ interface CategoryContentItem {
export class CategoryContentViewComponent implements OnInit {
private readonly activatedRoute = inject(ActivatedRoute);
private readonly router = inject(Router);
private readonly translate = inject(TranslateService);
private readonly catalog = inject(PORTAL_CATALOG_FACADE) as PortalCatalogFacade<
CategoryContentItem,
CategoryContentItem,
@@ -70,7 +71,18 @@ export class CategoryContentViewComponent implements OnInit {
readonly contentSortMode = this.catalog.contentSortMode;
readonly isPaginatedContentLoading =
this.catalog.isPaginatedContentLoading;
readonly isXtreamLoadingSubtitle = computed(
() =>
this.catalog.provider === 'xtream' &&
this.isPaginatedContentLoading()
);
readonly categoryItemSubtitle = computed(() => {
if (this.isXtreamLoadingSubtitle()) {
return this.translate.instant(
'WORKSPACE.SHELL.XTREAM_IMPORT_LOADING'
);
}
const itemCount = this.categoryItemCount();
return `${itemCount} ${itemCount === 1 ? 'item' : 'items'}`;
});
@@ -0,0 +1,6 @@
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
setupZoneTestEnv({
errorOnUnknownElements: true,
errorOnUnknownProperties: true,
});
+28
View File
@@ -0,0 +1,28 @@
{
"extends": "../../../../tsconfig.base.json",
"compilerOptions": {
"isolatedModules": true,
"target": "es2022",
"moduleResolution": "bundler",
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"emitDecoratorMetadata": false,
"module": "preserve"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"strictTemplates": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
}
]
}
@@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": []
},
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts"]
}
@@ -0,0 +1,17 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../../../dist/out-tsc",
"module": "commonjs",
"target": "es2016",
"types": ["jest", "node"],
"moduleResolution": "node10"
},
"files": ["src/test-setup.ts"],
"include": [
"jest.config.ts",
"src/**/*.test.ts",
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}