refactor(epg): gate multi epg queries by runtime capability

This commit is contained in:
4gray committed 2026-05-22 13:33:31 +03:00
1 parent 030c6ef89d
commit 54d29f2239
2 files changed
+105 -3

No files matched your search

@@ -1,4 +1,14 @@
import { isSelectedEpgDayToday } from './multi-epg-container.component';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { OverlayRef } from '@angular/cdk/overlay';
import { MatDialog } from '@angular/material/dialog';
import { TranslateService } from '@ngx-translate/core';
import { of } from 'rxjs';
import { RuntimeCapabilitiesService } from '@iptvnator/services';
import {
MultiEpgContainerComponent,
isSelectedEpgDayToday,
} from './multi-epg-container.component';
import { COMPONENT_OVERLAY_REF } from './overlay-ref.token';
describe('isSelectedEpgDayToday', () => {
it('returns true only when the selected EPG day is the actual current day', () => {
@@ -9,3 +19,87 @@ describe('isSelectedEpgDayToday', () => {
expect(isSelectedEpgDayToday('20260522', now)).toBe(false);
});
});
describe('MultiEpgContainerComponent runtime gates', () => {
let fixture: ComponentFixture<MultiEpgContainerComponent>;
let component: MultiEpgContainerComponent;
let runtimeCapabilities: { supportsEpg: boolean };
const originalElectron = window.electron;
beforeEach(async () => {
runtimeCapabilities = { supportsEpg: false };
await TestBed.configureTestingModule({
imports: [MultiEpgContainerComponent],
providers: [
{ provide: MatDialog, useValue: { open: jest.fn() } },
{
provide: COMPONENT_OVERLAY_REF,
useValue: { detach: jest.fn() },
},
{
provide: OverlayRef,
useValue: { detach: jest.fn() },
},
{
provide: RuntimeCapabilitiesService,
useValue: runtimeCapabilities,
},
{
provide: TranslateService,
useValue: {
currentLang: 'en',
defaultLang: 'en',
onLangChange: of(null),
},
},
],
})
.overrideComponent(MultiEpgContainerComponent, {
set: { template: '' },
})
.compileComponents();
fixture = TestBed.createComponent(MultiEpgContainerComponent);
component = fixture.componentInstance;
});
afterEach(() => {
window.electron = originalElectron;
fixture.destroy();
jest.restoreAllMocks();
jest.useRealTimers();
});
it('does not request EPG channel ranges when runtime EPG support is disabled', async () => {
const getEpgChannelsByRange = jest.fn().mockResolvedValue([]);
window.electron = {
...window.electron,
getEpgChannelsByRange,
} as unknown as typeof window.electron;
jest.spyOn(console, 'warn').mockImplementation(() => undefined);
await component.requestPrograms();
expect(getEpgChannelsByRange).not.toHaveBeenCalled();
expect(component.isLoading()).toBe(false);
});
it('does not search EPG programs when runtime EPG support is disabled', () => {
jest.useFakeTimers();
const searchEpgPrograms = jest.fn().mockResolvedValue([]);
window.electron = {
...window.electron,
searchEpgPrograms,
} as unknown as typeof window.electron;
component.onProgramSearchInput({
target: { value: 'news' },
} as unknown as Event);
jest.advanceTimersByTime(600);
expect(searchEpgPrograms).not.toHaveBeenCalled();
expect(component.isSearchingPrograms()).toBe(false);
expect(component.programSearchResults()).toEqual([]);
});
});
@@ -29,6 +29,7 @@ import {
EpgChannelWithPrograms,
EpgProgram,
} from '@iptvnator/shared/interfaces';
import { RuntimeCapabilitiesService } from '@iptvnator/services';
import { EpgItemDescriptionComponent } from '../epg-list/epg-item-description/epg-item-description.component';
import { COMPONENT_OVERLAY_REF } from './overlay-ref.token';
@@ -181,6 +182,7 @@ export class MultiEpgContainerComponent
private readonly dialog = inject(MatDialog);
private readonly overlayRef = inject<OverlayRef>(COMPONENT_OVERLAY_REF);
private readonly runtime = inject(RuntimeCapabilitiesService);
ngOnInit() {
// Update current time line every minute
@@ -267,8 +269,8 @@ export class MultiEpgContainerComponent
}
async requestPrograms(): Promise<void> {
if (!window.electron) {
console.warn('Multi-EPG not available: Electron not detected');
if (!this.runtime.supportsEpg) {
console.warn('Multi-EPG not available in this runtime');
return;
}
@@ -460,6 +462,12 @@ export class MultiEpgContainerComponent
return;
}
if (!this.runtime.supportsEpg) {
this.programSearchResults.set([]);
this.isSearchingPrograms.set(false);
return;
}
// Debounce search by 500ms - show spinner only when search actually starts
this.searchDebounceTimer = setTimeout(async () => {
this.isSearchingPrograms.set(true);