mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 10:06:15 -08:00
chore(test): clean lint and warning baseline
This commit is contained in:
1 parent
3d4d2ca9bf
commit
a1b0d9186c
9 files changed
+154
-60
No files matched your search
+19
-14
@@ -1,26 +1,31 @@
|
||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone/index.mjs';
|
||||
import { installDuplicateVideoJsQualityLevelsWarnFilter } from '@iptvnator/shared/testing';
|
||||
|
||||
Object.defineProperty(globalThis, 'jest', {
|
||||
configurable: true,
|
||||
value: import.meta.jest,
|
||||
configurable: true,
|
||||
value: import.meta.jest,
|
||||
});
|
||||
|
||||
if (typeof (globalThis as { IDBFactory?: unknown }).IDBFactory === 'undefined') {
|
||||
Object.defineProperty(globalThis, 'IDBFactory', {
|
||||
configurable: true,
|
||||
value: class MockIDBFactory {},
|
||||
});
|
||||
if (
|
||||
typeof (globalThis as { IDBFactory?: unknown }).IDBFactory === 'undefined'
|
||||
) {
|
||||
Object.defineProperty(globalThis, 'IDBFactory', {
|
||||
configurable: true,
|
||||
value: class MockIDBFactory {},
|
||||
});
|
||||
}
|
||||
|
||||
if (!Element.prototype.animate) {
|
||||
Element.prototype.animate = () =>
|
||||
({
|
||||
cancel: () => undefined,
|
||||
finished: Promise.resolve(),
|
||||
}) as unknown as Animation;
|
||||
Element.prototype.animate = () =>
|
||||
({
|
||||
cancel: () => undefined,
|
||||
finished: Promise.resolve(),
|
||||
}) as unknown as Animation;
|
||||
}
|
||||
|
||||
installDuplicateVideoJsQualityLevelsWarnFilter();
|
||||
|
||||
setupZoneTestEnv({
|
||||
errorOnUnknownElements: true,
|
||||
errorOnUnknownProperties: true,
|
||||
errorOnUnknownElements: true,
|
||||
errorOnUnknownProperties: true,
|
||||
});
|
||||
@@ -23,6 +23,8 @@ import { VodDetailsRouteComponent } from './vod-details-route.component';
|
||||
|
||||
describe('VodDetailsRouteComponent', () => {
|
||||
let fixture: ComponentFixture<VodDetailsRouteComponent>;
|
||||
let consoleDebugSpy: jest.SpyInstance | undefined;
|
||||
let consoleWarnSpy: jest.SpyInstance | undefined;
|
||||
const selectedItem = signal<XtreamVodDetails | null>(null);
|
||||
const isLoadingDetails = signal(false);
|
||||
const detailsError = signal<string | null>(null);
|
||||
@@ -47,6 +49,30 @@ describe('VodDetailsRouteComponent', () => {
|
||||
const getPlaybackPosition = jest.fn().mockResolvedValue(null);
|
||||
|
||||
beforeEach(async () => {
|
||||
const consoleDebug = console.debug.bind(console);
|
||||
const consoleWarn = console.warn.bind(console);
|
||||
consoleDebugSpy = jest
|
||||
.spyOn(console, 'debug')
|
||||
.mockImplementation((...args: unknown[]) => {
|
||||
if (args[0] === '[VodDetailsRoute]') {
|
||||
return;
|
||||
}
|
||||
|
||||
consoleDebug(...args);
|
||||
});
|
||||
consoleWarnSpy = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation((...args: unknown[]) => {
|
||||
if (
|
||||
args[0] === '[VodDetailsRoute]' &&
|
||||
args[1] === 'Deferring VOD details init: playlist not ready'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
consoleWarn(...args);
|
||||
});
|
||||
|
||||
selectedItem.set(null);
|
||||
isLoadingDetails.set(false);
|
||||
detailsError.set(null);
|
||||
@@ -166,6 +192,11 @@ describe('VodDetailsRouteComponent', () => {
|
||||
fixture = TestBed.createComponent(VodDetailsRouteComponent);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
consoleDebugSpy?.mockRestore();
|
||||
consoleWarnSpy?.mockRestore();
|
||||
});
|
||||
|
||||
it('renders an informational fallback without playback controls when Xtream returns empty metadata', () => {
|
||||
selectedItem.set({
|
||||
info: [],
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
computed,
|
||||
inject,
|
||||
} from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import {
|
||||
PlaylistErrorViewComponent,
|
||||
} from '@iptvnator/portal/shared/ui';
|
||||
import {
|
||||
XtreamContentInitBlockReason,
|
||||
XtreamStore,
|
||||
} from '@iptvnator/portal/xtream/data-access';
|
||||
import { PlaylistErrorViewComponent } from '@iptvnator/portal/shared/ui';
|
||||
import { XtreamStore } from '@iptvnator/portal/xtream/data-access';
|
||||
import { XtreamCachedOfflineNoticeComponent } from './xtream-cached-offline-notice.component';
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone';
|
||||
import { installDuplicateVideoJsQualityLevelsWarnFilter } from '@iptvnator/shared/testing';
|
||||
|
||||
installDuplicateVideoJsQualityLevelsWarnFilter();
|
||||
|
||||
setupZoneTestEnv({
|
||||
errorOnUnknownElements: true,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "shared-testing",
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/shared/testing/src",
|
||||
"projectType": "library",
|
||||
"tags": ["scope:shared", "domain:shared-runtime", "type:util"],
|
||||
"targets": {
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { installDuplicateVideoJsQualityLevelsWarnFilter } from './lib/console-warn-filters';
|
||||
@@ -0,0 +1,19 @@
|
||||
export function installDuplicateVideoJsQualityLevelsWarnFilter(): void {
|
||||
const originalConsoleWarn = console.warn.bind(console);
|
||||
|
||||
console.warn = (...args: unknown[]) => {
|
||||
const message = args
|
||||
.filter((arg): arg is string => typeof arg === 'string')
|
||||
.join(' ');
|
||||
|
||||
if (isDuplicateVideoJsQualityLevelsWarning(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
originalConsoleWarn(...args);
|
||||
};
|
||||
}
|
||||
|
||||
function isDuplicateVideoJsQualityLevelsWarning(message: string): boolean {
|
||||
return message.includes('A plugin named "qualityLevels" already exists.');
|
||||
}
|
||||
@@ -21,6 +21,8 @@ export type EmbeddedMpvBoundsProvider = (
|
||||
|
||||
const STALLED_TIMEOUT_MS = 30_000;
|
||||
|
||||
type ElectronBridge = Window['electron'];
|
||||
|
||||
@Injectable()
|
||||
export class EmbeddedMpvSessionController {
|
||||
readonly support = signal<EmbeddedMpvSupport | null>(null);
|
||||
@@ -144,7 +146,14 @@ export class EmbeddedMpvSessionController {
|
||||
return;
|
||||
}
|
||||
|
||||
const prepared = await window.electron!.prepareEmbeddedMpv?.();
|
||||
const electron = this.getElectronBridge();
|
||||
if (!electron) {
|
||||
throw new Error(
|
||||
'Embedded MPV requires the Electron desktop build.'
|
||||
);
|
||||
}
|
||||
|
||||
const prepared = await electron.prepareEmbeddedMpv?.();
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
@@ -158,24 +167,21 @@ export class EmbeddedMpvSessionController {
|
||||
this.support.set(prepared);
|
||||
}
|
||||
|
||||
const created = await window.electron!.createEmbeddedMpvSession(
|
||||
const created = await electron.createEmbeddedMpvSession(
|
||||
measureBounds(host),
|
||||
playback.title,
|
||||
initialVolume
|
||||
);
|
||||
|
||||
if (disposed) {
|
||||
await window.electron!.disposeEmbeddedMpvSession(created.id);
|
||||
await electron.disposeEmbeddedMpvSession(created.id);
|
||||
return;
|
||||
}
|
||||
|
||||
activeSessionId = created.id;
|
||||
this.sessionId.set(created.id);
|
||||
this.session.set(created);
|
||||
await window.electron!.loadEmbeddedMpvPlayback(
|
||||
created.id,
|
||||
playback
|
||||
);
|
||||
await electron.loadEmbeddedMpvPlayback(created.id, playback);
|
||||
scheduleBoundsSync();
|
||||
};
|
||||
|
||||
@@ -213,14 +219,12 @@ export class EmbeddedMpvSessionController {
|
||||
async togglePaused(): Promise<void> {
|
||||
const id = this.sessionId();
|
||||
const session = this.session();
|
||||
if (!id || !session || !window.electron?.setEmbeddedMpvPaused) {
|
||||
const electron = this.getElectronBridge();
|
||||
if (!id || !session || !electron?.setEmbeddedMpvPaused) {
|
||||
return;
|
||||
}
|
||||
const updated = await this.guardIpc(() =>
|
||||
window.electron!.setEmbeddedMpvPaused(
|
||||
id,
|
||||
session.status !== 'paused'
|
||||
)
|
||||
electron.setEmbeddedMpvPaused(id, session.status !== 'paused')
|
||||
);
|
||||
if (updated) {
|
||||
this.session.set(updated);
|
||||
@@ -230,12 +234,13 @@ export class EmbeddedMpvSessionController {
|
||||
async seekBy(deltaSeconds: number): Promise<boolean> {
|
||||
const id = this.sessionId();
|
||||
const session = this.session();
|
||||
if (!id || !session || !window.electron?.seekEmbeddedMpv) {
|
||||
const electron = this.getElectronBridge();
|
||||
if (!id || !session || !electron?.seekEmbeddedMpv) {
|
||||
return false;
|
||||
}
|
||||
const next = Math.max(0, session.positionSeconds + deltaSeconds);
|
||||
const updated = await this.guardIpc(() =>
|
||||
window.electron!.seekEmbeddedMpv(id, next)
|
||||
electron.seekEmbeddedMpv(id, next)
|
||||
);
|
||||
if (updated) {
|
||||
this.session.set(updated);
|
||||
@@ -245,11 +250,12 @@ export class EmbeddedMpvSessionController {
|
||||
|
||||
async seekTo(seconds: number): Promise<void> {
|
||||
const id = this.sessionId();
|
||||
if (!id || !window.electron?.seekEmbeddedMpv) {
|
||||
const electron = this.getElectronBridge();
|
||||
if (!id || !electron?.seekEmbeddedMpv) {
|
||||
return;
|
||||
}
|
||||
const updated = await this.guardIpc(() =>
|
||||
window.electron!.seekEmbeddedMpv(id, seconds)
|
||||
electron.seekEmbeddedMpv(id, seconds)
|
||||
);
|
||||
if (updated) {
|
||||
this.session.set(updated);
|
||||
@@ -258,11 +264,12 @@ export class EmbeddedMpvSessionController {
|
||||
|
||||
async applyVolume(value: number): Promise<void> {
|
||||
const id = this.sessionId();
|
||||
if (!id || !window.electron?.setEmbeddedMpvVolume) {
|
||||
const electron = this.getElectronBridge();
|
||||
if (!id || !electron?.setEmbeddedMpvVolume) {
|
||||
return;
|
||||
}
|
||||
const updated = await this.guardIpc(() =>
|
||||
window.electron!.setEmbeddedMpvVolume(id, value)
|
||||
electron.setEmbeddedMpvVolume(id, value)
|
||||
);
|
||||
if (updated) {
|
||||
this.session.set(updated);
|
||||
@@ -271,11 +278,12 @@ export class EmbeddedMpvSessionController {
|
||||
|
||||
async setAudioTrack(trackId: number): Promise<void> {
|
||||
const id = this.sessionId();
|
||||
if (!id || !window.electron?.setEmbeddedMpvAudioTrack) {
|
||||
const electron = this.getElectronBridge();
|
||||
if (!id || !electron?.setEmbeddedMpvAudioTrack) {
|
||||
return;
|
||||
}
|
||||
const updated = await this.guardIpc(() =>
|
||||
window.electron!.setEmbeddedMpvAudioTrack(id, trackId)
|
||||
electron.setEmbeddedMpvAudioTrack(id, trackId)
|
||||
);
|
||||
if (updated) {
|
||||
this.session.set(updated);
|
||||
@@ -284,11 +292,13 @@ export class EmbeddedMpvSessionController {
|
||||
|
||||
async setSubtitleTrack(trackId: number): Promise<void> {
|
||||
const id = this.sessionId();
|
||||
if (!id || !window.electron?.setEmbeddedMpvSubtitleTrack) {
|
||||
const electron = this.getElectronBridge();
|
||||
if (!id || !electron?.setEmbeddedMpvSubtitleTrack) {
|
||||
return;
|
||||
}
|
||||
const setSubtitleTrack = electron.setEmbeddedMpvSubtitleTrack;
|
||||
const updated = await this.guardIpc(() =>
|
||||
window.electron!.setEmbeddedMpvSubtitleTrack!(id, trackId)
|
||||
setSubtitleTrack(id, trackId)
|
||||
);
|
||||
if (updated) {
|
||||
this.session.set(updated);
|
||||
@@ -297,12 +307,12 @@ export class EmbeddedMpvSessionController {
|
||||
|
||||
async setSpeed(speed: number): Promise<void> {
|
||||
const id = this.sessionId();
|
||||
if (!id || !window.electron?.setEmbeddedMpvSpeed) {
|
||||
const electron = this.getElectronBridge();
|
||||
if (!id || !electron?.setEmbeddedMpvSpeed) {
|
||||
return;
|
||||
}
|
||||
const updated = await this.guardIpc(() =>
|
||||
window.electron!.setEmbeddedMpvSpeed!(id, speed)
|
||||
);
|
||||
const setSpeed = electron.setEmbeddedMpvSpeed;
|
||||
const updated = await this.guardIpc(() => setSpeed(id, speed));
|
||||
if (updated) {
|
||||
this.session.set(updated);
|
||||
}
|
||||
@@ -310,12 +320,12 @@ export class EmbeddedMpvSessionController {
|
||||
|
||||
async setAspect(aspect: string): Promise<void> {
|
||||
const id = this.sessionId();
|
||||
if (!id || !window.electron?.setEmbeddedMpvAspect) {
|
||||
const electron = this.getElectronBridge();
|
||||
if (!id || !electron?.setEmbeddedMpvAspect) {
|
||||
return;
|
||||
}
|
||||
const updated = await this.guardIpc(() =>
|
||||
window.electron!.setEmbeddedMpvAspect!(id, aspect)
|
||||
);
|
||||
const setAspect = electron.setEmbeddedMpvAspect;
|
||||
const updated = await this.guardIpc(() => setAspect(id, aspect));
|
||||
if (updated) {
|
||||
this.session.set(updated);
|
||||
}
|
||||
@@ -326,15 +336,17 @@ export class EmbeddedMpvSessionController {
|
||||
title: string
|
||||
): Promise<EmbeddedMpvSession['recording'] | null> {
|
||||
const id = this.sessionId();
|
||||
if (!id || !window.electron?.startEmbeddedMpvRecording) {
|
||||
const electron = this.getElectronBridge();
|
||||
if (!id || !electron?.startEmbeddedMpvRecording) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const startEmbeddedMpvRecording = electron.startEmbeddedMpvRecording;
|
||||
const resolvedDirectory =
|
||||
directory?.trim() ||
|
||||
(await window.electron.getEmbeddedMpvDefaultRecordingFolder?.());
|
||||
(await electron.getEmbeddedMpvDefaultRecordingFolder?.());
|
||||
const updated = await this.guardIpc(() =>
|
||||
window.electron!.startEmbeddedMpvRecording!(id, {
|
||||
startEmbeddedMpvRecording(id, {
|
||||
directory: resolvedDirectory,
|
||||
title,
|
||||
})
|
||||
@@ -348,12 +360,12 @@ export class EmbeddedMpvSessionController {
|
||||
|
||||
async stopRecording(): Promise<EmbeddedMpvSession['recording'] | null> {
|
||||
const id = this.sessionId();
|
||||
if (!id || !window.electron?.stopEmbeddedMpvRecording) {
|
||||
const electron = this.getElectronBridge();
|
||||
if (!id || !electron?.stopEmbeddedMpvRecording) {
|
||||
return null;
|
||||
}
|
||||
const updated = await this.guardIpc(() =>
|
||||
window.electron!.stopEmbeddedMpvRecording!(id)
|
||||
);
|
||||
const stopEmbeddedMpvRecording = electron.stopEmbeddedMpvRecording;
|
||||
const updated = await this.guardIpc(() => stopEmbeddedMpvRecording(id));
|
||||
if (updated) {
|
||||
this.session.set(updated);
|
||||
return updated.recording ?? null;
|
||||
@@ -373,7 +385,13 @@ export class EmbeddedMpvSessionController {
|
||||
|
||||
private async loadSupport(): Promise<void> {
|
||||
try {
|
||||
this.support.set(await window.electron!.getEmbeddedMpvSupport());
|
||||
const electron = this.getElectronBridge();
|
||||
if (!electron?.getEmbeddedMpvSupport) {
|
||||
throw new Error(
|
||||
'Embedded MPV requires the Electron desktop build.'
|
||||
);
|
||||
}
|
||||
this.support.set(await electron.getEmbeddedMpvSupport());
|
||||
} catch (error) {
|
||||
this.support.set({
|
||||
supported: false,
|
||||
@@ -383,6 +401,10 @@ export class EmbeddedMpvSessionController {
|
||||
}
|
||||
}
|
||||
|
||||
private getElectronBridge(): ElectronBridge | undefined {
|
||||
return window.electron;
|
||||
}
|
||||
|
||||
private handleStalledTracking(
|
||||
status: EmbeddedMpvSession['status'] | null
|
||||
): void {
|
||||
|
||||
@@ -79,6 +79,7 @@
|
||||
"@iptvnator/shared/m3u-utils": [
|
||||
"libs/shared/m3u-utils/src/index.ts"
|
||||
],
|
||||
"@iptvnator/shared/testing": ["libs/shared/testing/src/index.ts"],
|
||||
"@iptvnator/services": ["libs/services/src/index.ts"],
|
||||
"@iptvnator/shared/interfaces": [
|
||||
"libs/shared/interfaces/src/index.ts"
|
||||
|
||||
Reference in new issue
Block a user