fix(videojs): harden MPEG-TS reset lifecycle

This commit is contained in:
4gray committed 2026-07-16 22:46:02 +02:00
1 parent 7de979a4b1
commit 3dd71d7d05
5 files changed
+72 -3

No files matched your search

@@ -64,6 +64,24 @@ describe('VjsMpegTsSession', () => {
expect(mpegTsPlayer.play).toHaveBeenCalledTimes(1);
});
it('handles an asynchronous autoplay rejection', () => {
const catchRejection = jest.fn();
const mpegTsPlayer = createMpegTsPlayer();
mpegTsPlayer.play.mockReturnValue({
catch: catchRejection,
} as unknown as Promise<void>);
createPlayerMock.mockReturnValue(mpegTsPlayer);
const video = document.createElement('video');
const { session } = createSession();
session.start('https://example.test/live/stream.ts', video);
expect(catchRejection).toHaveBeenCalledWith(expect.any(Function));
expect(
catchRejection.mock.calls[0][0](new Error('autoplay blocked'))
).toBeUndefined();
});
it('normalizes VOD duration from seekable and then buffered ranges', async () => {
const mpegTsPlayer = createMpegTsPlayer();
createPlayerMock.mockReturnValue(mpegTsPlayer);
@@ -80,7 +80,10 @@ export class VjsMpegTsSession {
}
engine.on(mpegts.Events.ERROR, this.errorListener);
engine.load();
void engine.play();
const playResult = engine.play();
if (playResult) {
void playResult.catch(() => undefined);
}
}
syncDuration(): void {
@@ -21,6 +21,18 @@ describe('VjsPlayerResetCoordinator', () => {
expect(harness.reset).toHaveBeenCalledTimes(1);
});
it('captures the latest engine volume after an asynchronous pause', () => {
const harness = createHarness(false, 0.3);
const coordinator = createCoordinator(harness);
coordinator.requestReset();
harness.player.volume(0.8);
harness.paused = true;
coordinator.handlePause();
expect(coordinator.handlePlayerReset()).toBe(0.8);
});
it('snapshots actual engine volume and suppresses reset-generated changes', () => {
const harness = createHarness(true, 0.3);
const coordinator = createCoordinator(harness, 0.8);
@@ -24,7 +24,6 @@ export class VjsPlayerResetCoordinator {
}
this.requested = true;
this.captureVolume();
try {
this.config.player().pause();
} catch {
@@ -99,6 +98,7 @@ export class VjsPlayerResetCoordinator {
return;
}
this.captureVolume();
this.requested = false;
this.inFlight = true;
try {
@@ -32,6 +32,7 @@ describe('VjsPlayerComponent reset lifecycle', () => {
});
beforeEach(async () => {
localStorage.removeItem('volume');
playerHarness = createVideoJsPlayerHarness();
videoJsMock
.mockReset()
@@ -150,6 +151,34 @@ describe('VjsPlayerComponent reset lifecycle', () => {
expect(playerHarness.reset).toHaveBeenCalledTimes(1);
});
it('restores a volume input that changes while reset waits for pause', () => {
localStorage.setItem('volume', '0.3');
fixture.componentRef.setInput('options', {
sources: [{ src: 'https://example.test/movie.mp4' }],
});
fixture.componentRef.setInput('volume', 0.3);
fixture.detectChanges();
playerHarness.ready();
playerHarness.paused = false;
playerHarness.pauseCompletesImmediately = false;
fixture.componentRef.setInput('options', { sources: [] });
fixture.detectChanges();
fixture.componentRef.setInput('volume', 0.8);
fixture.detectChanges();
playerHarness.emit('volumechange');
playerHarness.paused = true;
playerHarness.emit('pause');
playerHarness.volume(1);
playerHarness.emit('volumechange');
playerHarness.currentVideo = document.createElement('video');
playerHarness.emit('playerreset');
expect(localStorage.getItem('volume')).toBe('0.8');
expect(playerHarness.volume).toHaveBeenLastCalledWith(0.8);
});
it('restores the latest normal source after an already-started reset', () => {
fixture.componentRef.setInput('options', {
sources: [{ src: 'https://example.test/movie.mp4' }],
@@ -259,6 +288,7 @@ describe('VjsPlayerComponent reset lifecycle', () => {
function createVideoJsPlayerHarness() {
const listeners = new Map<string, Set<() => void>>();
let volume = 0.5;
const harness = {
currentVideo: document.createElement('video'),
paused: true,
@@ -272,6 +302,12 @@ function createVideoJsPlayerHarness() {
}),
reset: jest.fn(),
src: jest.fn(),
volume: jest.fn((value?: number) => {
if (value !== undefined) {
volume = value;
}
return volume;
}),
emit(event: string) {
for (const listener of listeners.get(event) ?? []) {
listener();
@@ -300,7 +336,7 @@ function createVideoJsPlayerHarness() {
reset: harness.reset,
src: harness.src,
tech: jest.fn(() => ({ el: () => harness.currentVideo })),
volume: jest.fn(() => 0.5),
volume: harness.volume,
dispose: jest.fn(),
qualitySelectorHls: jest.fn(),
aspectRatioPanel: jest.fn(),