Files
iptvnator/libs/ui/components/src/lib/content-hero/content-hero.component.spec.ts
T
4gray 760099358b feat(downloads): redesign download manager (#1313)
* docs(downloads): specify manager MVP redesign

* docs(downloads): plan manager MVP implementation

* docs(downloads): tighten manager validation plan

* fix(downloads): keep renderer download state global

* fix(downloads): make active count accessible

* feat(downloads): derive queue and library view model

* test(downloads): close view model coverage gaps

* fix(downloads): stabilize malformed view model data

* refactor(downloads): isolate library navigation

* fix(downloads): report library navigation failures

* feat(downloads): add ready-to-watch library

* feat(downloads): add active download queue

* feat(downloads): finish manager MVP

* docs(downloads): clarify detail-first offline behavior

* docs(downloads): plan detail navigation follow-up

* fix(downloads): open completed movies in details

* test(downloads): cover pending series navigation

* fix(downloads): honor the global cover size

* fix(downloads): prefer local playback in shared details

* fix(downloads): preserve external launch priority

* fix(downloads): prefer local playback in Xtream details

* test(downloads): cover offline detail journey

* docs(downloads): document offline detail behavior

* docs(downloads): format detail navigation plan

* fix(downloads): open Stalker items in provider details

* docs(downloads): clarify Stalker navigation fallback

* fix(xtream): isolate reused detail identities

* fix(xtream): ignore stale VOD positions

* fix(downloads): keep offline Xtream playback available

* docs(downloads): clarify provider playback availability

* docs(downloads): design missing-file recovery

* docs(downloads): plan missing-file recovery

* feat(downloads): derive completed file availability

* feat(downloads): recover missing completed files

* feat(downloads): refresh missing local files

* feat(downloads): separate missing files from ready media

* feat(downloads): surface missing files for recovery

* refactor(downloads): simplify ready cards

* test(downloads): cover missing-file and series journeys

* feat(downloads): finish missing-file recovery

* docs(downloads): design offline detail views

* docs(downloads): plan offline detail views

* feat(downloads): persist offline metadata snapshots

* fix(downloads): complete metadata snapshot bridge contract

* feat(downloads): manage offline metadata snapshots

* fix(downloads): harden metadata snapshot updates

* fix(downloads): restrict snapshot artwork

* fix(downloads): guard restart artwork URL

* fix(downloads): refine artwork URL checks

* feat(downloads): expose offline metadata updates

* fix(downloads): keep metadata service change focused

* fix(downloads): preserve metadata error conventions

* feat(downloads): derive offline detail content

* fix(downloads): preserve unknown episode coordinates

* feat(downloads): add focused offline detail routes

* fix(downloads): ignore fragments in shell route state

* fix(downloads): normalize fragments before queries

* feat(downloads): open ready cards in offline details

* fix(downloads): use native disabled card styles

* feat(downloads): enrich offline detail metadata

* fix(downloads): harden offline metadata resolution

* fix(downloads): preserve stalker provider titles

* fix(downloads): distinguish stalker metadata seeds

* fix(downloads): stabilize offline metadata refresh

* fix(downloads): throttle sparse metadata refreshes

* fix(downloads): type metadata language settings

* feat(downloads): render offline movie and series details

* fix(downloads): harden offline detail interactions

* fix(downloads): close offline detail edge cases

* feat(downloads): hand off to provider-only details

* fix(downloads): preserve stalker provider handoff

* feat(downloads): capture metadata at download time

* fix(downloads): preserve snapshot source semantics

* fix(downloads): preserve episode snapshot identity

* docs(downloads): document offline details flow

* docs(downloads): clarify stalker provider fallback

* test(downloads): cover offline detail journeys

* test(downloads): stabilize offline detail selectors

* style(downloads): format changed files

* docs(downloads): clean design spec formatting

* fix(downloads): preserve offline library ownership

* test(downloads): fix Windows workspace navigation

* test(database): preserve Electron tsconfig resolution

* perf(downloads): avoid blocking file availability probes
2026-08-01 18:09:31 +02:00

130 lines
4.7 KiB
TypeScript

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { ContentHeroComponent } from './content-hero.component';
describe('ContentHeroComponent', () => {
let fixture: ComponentFixture<ContentHeroComponent>;
const originalResizeObserver = globalThis.ResizeObserver;
afterEach(() => {
Object.defineProperty(globalThis, 'ResizeObserver', {
configurable: true,
writable: true,
value: originalResizeObserver,
});
});
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [ContentHeroComponent, TranslateModule.forRoot()],
}).compileComponents();
fixture = TestBed.createComponent(ContentHeroComponent);
const translate = TestBed.inject(TranslateService);
translate.setTranslation('en', { BACK: 'Go back' });
translate.use('en');
});
it('renders description content when ResizeObserver is unavailable', () => {
Object.defineProperty(globalThis, 'ResizeObserver', {
configurable: true,
writable: true,
value: undefined,
});
fixture.componentRef.setInput('title', 'Fallback_Title');
fixture.componentRef.setInput('description', 'Plain description');
expect(() => fixture.detectChanges()).not.toThrow();
const host = fixture.nativeElement as HTMLElement;
expect(host.textContent).toContain('Fallback Title');
expect(host.textContent).toContain('Plain description');
});
it('renders an accessible button with the translated fallback back label', () => {
fixture.detectChanges();
const back = (fixture.nativeElement as HTMLElement).querySelector(
'.hero__back-button'
) as HTMLButtonElement;
expect(back.type).toBe('button');
expect(back.getAttribute('aria-label')).toBe('Go back');
});
it('uses an explicit back label when supplied', () => {
fixture.componentRef.setInput('backLabel', 'Back to downloads');
fixture.detectChanges();
const back = (fixture.nativeElement as HTMLElement).querySelector(
'.hero__back-button'
) as HTMLButtonElement;
expect(back.getAttribute('aria-label')).toBe('Back to downloads');
});
it('resets a poster failure when the poster URL changes', () => {
fixture.componentRef.setInput('posterUrl', 'broken.jpg');
fixture.detectChanges();
const first = (fixture.nativeElement as HTMLElement).querySelector(
'.poster img[src="broken.jpg"]'
) as HTMLImageElement;
first.dispatchEvent(new Event('error'));
fixture.detectChanges();
expect(
(fixture.nativeElement as HTMLElement).querySelector(
'.poster img[src="broken.jpg"]'
)
).toBeNull();
fixture.componentRef.setInput('posterUrl', 'replacement.jpg');
fixture.detectChanges();
expect(
(fixture.nativeElement as HTMLElement).querySelector(
'.poster img[src="replacement.jpg"]'
)
).toBeTruthy();
});
it('keeps an explicit backdrop visible when the poster image fails', () => {
fixture.componentRef.setInput('posterUrl', 'broken-poster.jpg');
fixture.componentRef.setInput('backdropUrl', 'working-backdrop.jpg');
fixture.detectChanges();
const poster = (fixture.nativeElement as HTMLElement).querySelector(
'img[src="broken-poster.jpg"]'
) as HTMLImageElement;
poster.dispatchEvent(new Event('error'));
fixture.detectChanges();
expect(
(fixture.nativeElement as HTMLElement).querySelector(
'img.hero__backdrop-image[src="working-backdrop.jpg"]'
)
).toBeTruthy();
});
it('falls back the backdrop independently without hiding a valid poster', () => {
fixture.componentRef.setInput('posterUrl', 'working-poster.jpg');
fixture.componentRef.setInput('backdropUrl', 'broken-backdrop.jpg');
fixture.detectChanges();
const backdrop = (fixture.nativeElement as HTMLElement).querySelector(
'img.hero__backdrop-image'
) as HTMLImageElement | null;
expect(backdrop).toBeTruthy();
backdrop?.dispatchEvent(new Event('error'));
fixture.detectChanges();
expect(
(fixture.nativeElement as HTMLElement).querySelector(
'.poster img[src="working-poster.jpg"]'
)
).toBeTruthy();
expect(
(fixture.nativeElement as HTMLElement).querySelector(
'img.hero__backdrop-image'
)
).toBeNull();
});
});