mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
refactor(playlist): use runtime capability for xtream sections
This commit is contained in:
1 parent
e5e58d8b33
commit
61cfe64951
4 files changed
+19
-17
No files matched your search
@@ -10,6 +10,7 @@ import {
|
||||
} from '@iptvnator/m3u-state';
|
||||
import { Subject } from 'rxjs';
|
||||
import { PlaylistMeta } from '@iptvnator/shared/interfaces';
|
||||
import { RuntimeCapabilitiesService } from '@iptvnator/services';
|
||||
import {
|
||||
PlaylistContextFacade,
|
||||
PlaylistRouteContext,
|
||||
@@ -43,7 +44,7 @@ describe('PlaylistContextFacade', () => {
|
||||
let playlistsSignal: ReturnType<typeof signal<PlaylistMeta[]>>;
|
||||
let loadedSignal: ReturnType<typeof signal<boolean>>;
|
||||
let activePlaylistIdSignal: ReturnType<typeof signal<string | null>>;
|
||||
let originalElectron: unknown;
|
||||
let runtimeCapabilities: { supportsXtreamSectionNavigation: boolean };
|
||||
|
||||
const xtreamA = createPlaylist({
|
||||
_id: 'xtream-a',
|
||||
@@ -82,13 +83,6 @@ describe('PlaylistContextFacade', () => {
|
||||
url: 'http://example.test/b.m3u',
|
||||
});
|
||||
|
||||
function setElectronAvailability(enabled: boolean): void {
|
||||
Object.defineProperty(window, 'electron', {
|
||||
configurable: true,
|
||||
value: enabled ? {} : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
function instantiateFacade(): PlaylistContextFacade {
|
||||
facade = TestBed.inject(PlaylistContextFacade);
|
||||
dispatch.mockClear();
|
||||
@@ -98,7 +92,6 @@ describe('PlaylistContextFacade', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
originalElectron = (window as Window & { electron?: unknown }).electron;
|
||||
|
||||
routerEvents = new Subject<NavigationEnd>();
|
||||
router = {
|
||||
@@ -117,7 +110,7 @@ describe('PlaylistContextFacade', () => {
|
||||
]);
|
||||
loadedSignal = signal(true);
|
||||
activePlaylistIdSignal = signal<string | null>(xtreamA._id);
|
||||
setElectronAvailability(true);
|
||||
runtimeCapabilities = { supportsXtreamSectionNavigation: true };
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
@@ -147,16 +140,16 @@ describe('PlaylistContextFacade', () => {
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: RuntimeCapabilitiesService,
|
||||
useValue: runtimeCapabilities,
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
localStorage.clear();
|
||||
Object.defineProperty(window, 'electron', {
|
||||
configurable: true,
|
||||
value: originalElectron,
|
||||
});
|
||||
routerEvents.complete();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
@@ -353,8 +346,8 @@ describe('PlaylistContextFacade', () => {
|
||||
).toEqual(['workspace', 'stalker', stalkerB._id, 'search']);
|
||||
});
|
||||
|
||||
it('omits Xtream sections outside Electron where section navigation is unsupported', () => {
|
||||
setElectronAvailability(false);
|
||||
it('omits Xtream sections when runtime section navigation is unsupported', () => {
|
||||
runtimeCapabilities.supportsXtreamSectionNavigation = false;
|
||||
const service = instantiateFacade();
|
||||
|
||||
expect(
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
PortalProvider,
|
||||
PortalRailSection,
|
||||
} from '@iptvnator/portal/shared/util';
|
||||
import { RuntimeCapabilitiesService } from '@iptvnator/services';
|
||||
|
||||
export interface PlaylistRouteContext {
|
||||
inWorkspace: boolean;
|
||||
@@ -69,6 +70,7 @@ const M3U_SECTIONS = ['all', 'groups', 'favorites', 'recent'] as const;
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class PlaylistContextFacade {
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly runtime = inject(RuntimeCapabilitiesService);
|
||||
private readonly router = inject(Router);
|
||||
private readonly store = inject(Store);
|
||||
|
||||
@@ -346,7 +348,7 @@ export class PlaylistContextFacade {
|
||||
|
||||
private supportsSectionNavigation(provider: PortalProvider): boolean {
|
||||
if (provider === 'xtreams') {
|
||||
return Boolean(window.electron);
|
||||
return this.runtime.supportsXtreamSectionNavigation;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -28,6 +28,7 @@ describe('RuntimeCapabilitiesService', () => {
|
||||
expect(service.supportsEmbeddedMpv).toBe(false);
|
||||
expect(service.supportsDesktopFileSave).toBe(false);
|
||||
expect(service.supportsRemoteControl).toBe(false);
|
||||
expect(service.supportsXtreamSectionNavigation).toBe(false);
|
||||
});
|
||||
|
||||
it('reports Electron capabilities from the available preload bridge methods', () => {
|
||||
@@ -66,6 +67,7 @@ describe('RuntimeCapabilitiesService', () => {
|
||||
expect(service.supportsEmbeddedMpv).toBe(true);
|
||||
expect(service.supportsDesktopFileSave).toBe(true);
|
||||
expect(service.supportsRemoteControl).toBe(true);
|
||||
expect(service.supportsXtreamSectionNavigation).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps feature-specific capabilities false when an Electron bridge is partial', () => {
|
||||
@@ -85,6 +87,7 @@ describe('RuntimeCapabilitiesService', () => {
|
||||
expect(service.supportsEmbeddedMpv).toBe(false);
|
||||
expect(service.supportsDesktopFileSave).toBe(false);
|
||||
expect(service.supportsRemoteControl).toBe(false);
|
||||
expect(service.supportsXtreamSectionNavigation).toBe(true);
|
||||
});
|
||||
|
||||
it('reads the bridge dynamically so tests and late preload setup stay accurate', () => {
|
||||
|
||||
@@ -81,6 +81,10 @@ export class RuntimeCapabilitiesService {
|
||||
);
|
||||
}
|
||||
|
||||
get supportsXtreamSectionNavigation(): boolean {
|
||||
return this.isElectron;
|
||||
}
|
||||
|
||||
private hasElectronMethod(methodName: string): boolean {
|
||||
return typeof this.electronBridge?.[methodName] === 'function';
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user