diff --git a/jest.web-esm.workspace.ts b/jest.web-esm.workspace.ts index d22c06e22..0bb8da563 100644 --- a/jest.web-esm.workspace.ts +++ b/jest.web-esm.workspace.ts @@ -13,6 +13,12 @@ export default { ...angularEsmPreset, rootDir: '.', roots: ['/apps/web', '/libs'], + // Jest's 5s default is thin for Angular component specs: TestBed compiles + // and instantiates a real component tree per test, and CI runners are far + // slower per-core than a dev machine. A starved worker then fails a test + // that is merely slow, with no defect behind it. Raised as headroom only — + // a spec that genuinely hangs still fails, just later. + testTimeout: 15_000, setupFilesAfterEnv: ['/apps/web/src/test-setup.ts'], resolver: nxPreset.resolver, moduleFileExtensions: Array.from( diff --git a/libs/ui/playback/src/lib/vod-details/vod-details.component.spec.ts b/libs/ui/playback/src/lib/vod-details/vod-details.component.spec.ts index a43109eb2..157a94e73 100644 --- a/libs/ui/playback/src/lib/vod-details/vod-details.component.spec.ts +++ b/libs/ui/playback/src/lib/vod-details/vod-details.component.spec.ts @@ -1,8 +1,21 @@ -import { signal } from '@angular/core'; +import { Component, input, output, signal } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { Router } from '@angular/router'; -import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { + TranslateModule, + TranslatePipe, + TranslateService, +} from '@ngx-translate/core'; +import { MatIcon } from '@angular/material/icon'; +import { SafePipe } from '@iptvnator/pipes'; +import { + DetailActionsTemplateDirective, + DetailMetaTemplateDirective, + DetailTagsTemplateDirective, + PortalDetailShellComponent, + ViewInPortalActionComponent, +} from '@iptvnator/ui/components'; import { PORTAL_EXTERNAL_PLAYBACK } from '@iptvnator/portal/shared/util'; import { CrossPortalSimilarService, @@ -23,6 +36,29 @@ jest.unstable_mockModule('@yangkghjh/videojs-aspect-ratio-panel', () => ({})); jest.unstable_mockModule('videojs-contrib-quality-levels', () => ({})); jest.unstable_mockModule('videojs-quality-selector-hls', () => ({})); +/** + * Stand-in for the inline player. The real one imports + * `WebPlayerViewComponent`, which drags ArtPlayer, video.js and the + * embedded-MPV bridge into this suite and instantiates that whole tree on + * every `createComponent`. These specs only assert what the host *hands* the + * player, so mirroring the selector and the template's bindings is enough — + * and it keeps the per-test budget clear of Jest's timeout on slower CI + * hardware. + */ +@Component({ + selector: 'app-portal-inline-player', + template: '
', +}) +class StubPortalInlinePlayerComponent { + readonly playbackSessionKey = input(''); + readonly playback = input(null); + readonly timeUpdate = output<{ currentTime: number; duration: number }>(); + readonly closed = output(); + readonly backClicked = output(); + readonly streamUrlCopied = output(); + readonly externalFallbackRequested = output(); +} + const STALKER_VOD: VodDetailsItem = createStalkerVodItem( { id: '42', @@ -250,7 +286,25 @@ describe('VodDetailsComponent offline playback', () => { }, }, ], - }).compileComponents(); + }) + .overrideComponent(VodDetailsComponent, { + // Same import list the component declares, with the inline + // player swapped for the stub above. + set: { + imports: [ + DetailActionsTemplateDirective, + DetailMetaTemplateDirective, + DetailTagsTemplateDirective, + MatIcon, + PortalDetailShellComponent, + ViewInPortalActionComponent, + StubPortalInlinePlayerComponent, + SafePipe, + TranslatePipe, + ], + }, + }) + .compileComponents(); const translate = TestBed.inject(TranslateService); translate.setTranslation('en', {