From da35d11c8b92b057b37e9b4feec7ef3367007417 Mon Sep 17 00:00:00 2001 From: Sebastian Piquerez Date: Mon, 22 Sep 2025 15:34:18 -0300 Subject: [PATCH 1/2] test: add unit test for switchToAlternativeContent and prebufferAlternativeContent --- src/streaming/MediaManager.js | 2 +- .../test/streaming/streaming.MediaManager.js | 58 ++++++++++++++++--- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/streaming/MediaManager.js b/src/streaming/MediaManager.js index eb3df7af32..aa12691c05 100644 --- a/src/streaming/MediaManager.js +++ b/src/streaming/MediaManager.js @@ -220,7 +220,7 @@ function MediaManager() { } altPlayer.play(); - logger.info('Alternative content playback started'); + logger.info(`Alternative content playback started for player ${playerId}`); isSwitching = false; } diff --git a/test/unit/test/streaming/streaming.MediaManager.js b/test/unit/test/streaming/streaming.MediaManager.js index e2460009e2..b4be56eec2 100644 --- a/test/unit/test/streaming/streaming.MediaManager.js +++ b/test/unit/test/streaming/streaming.MediaManager.js @@ -11,12 +11,10 @@ describe('MediaManager', function () { let debugMock; beforeEach(function () { - // Setup mocks videoModelMock = new VideoModelMock(); playbackControllerMock = new PlaybackControllerMock(); debugMock = new DebugMock(); - // Configure MediaManager mediaManager.setConfig({ videoModel: videoModelMock, playbackController: playbackControllerMock, @@ -44,24 +42,70 @@ describe('MediaManager', function () { }); }); + describe('prebufferAlternativeContent', function () { + it('should start prebuffering alternative content and log the action', function () { + const testUrl = 'http://test.mpd'; + const testPlayerId = 'testPlayer'; + + mediaManager.prebufferAlternativeContent(testPlayerId, testUrl); + + expect(debugMock.log.info).to.equal(`Starting prebuffering for player ${testPlayerId}`); + }); + }); + + describe('switchToAlternativeContent', function () { + beforeEach(function () { + const mockVideoElement = videoModelMock.getElement(); + mediaManager.setAlternativeVideoElement(mockVideoElement); + }); + + it('should switch to alternative content without prebuffered content', function () { + const testUrl = 'http://test.mpd'; + const testPlayerId = 'testPlayer'; + + mediaManager.switchToAlternativeContent(testPlayerId, testUrl); + + expect(debugMock.log.info).to.equal(`Alternative content playback started for player ${testPlayerId}`); + }); + + it('should switch to alternative content with prebuffered content', function () { + const testUrl = 'http://test.mpd'; + const testPlayerId = 'testPlayer'; + + mediaManager.prebufferAlternativeContent(testPlayerId, testUrl); + expect(debugMock.log.info).to.equal(`Starting prebuffering for player ${testPlayerId}`); + + mediaManager.switchToAlternativeContent(testPlayerId, testUrl); + expect(debugMock.log.info).to.equal(`Alternative content playback started for player ${testPlayerId}`); + }); + + it('should switch to alternative content and seek to a given time', function () { + const testUrl = 'http://test.mpd'; + const testPlayerId = 'testPlayer'; + const testTime = 15; + + mediaManager.switchToAlternativeContent(testPlayerId, testUrl, testTime); + + expect(debugMock.log.debug).to.equal(`Seeking alternative content to time: ${testTime}`); + expect(debugMock.log.info).to.equal(`Alternative content playback started for player ${testPlayerId}`); + }); + }); + describe('getAlternativePlayer', function () { beforeEach(function () { const mockVideoElement = videoModelMock.getElement(); mediaManager.setAlternativeVideoElement(mockVideoElement); }); - it('should return undefined when no alternative player is set', function () { + it('should return null when no alternative player is set', function () { const result = mediaManager.getAlternativePlayer(); - expect(result).to.be.undefined; + expect(result).to.be.null; }); it('should return the alternative player when it is set', function () { - // Initialize an alternative player by calling initializeAlternativePlayer - // We need to access the private method through switchToAlternativeContent const testUrl = 'http://test.mpd'; const testPlayerId = 'testPlayer'; - // Trigger initialization of alternative player mediaManager.switchToAlternativeContent(testPlayerId, testUrl, 0); const result = mediaManager.getAlternativePlayer(); From 04f67e0526a2ba38e0792ce2538b1819e6ebb3b3 Mon Sep 17 00:00:00 2001 From: Sebastian Piquerez Date: Tue, 23 Sep 2025 11:47:49 -0300 Subject: [PATCH 2/2] Revert "Merge branch 'alternative-media-presentations/media-manager-unit-tests' into media-manager-unit-test/prebuffer-and-switch-alternative-content" This reverts commit 90541aa8287f0e75cc9d8fc387483de6eed319a6, reversing changes made to da35d11c8b92b057b37e9b4feec7ef3367007417. --- .claude/settings.local.json | 11 --- CLAUDE.md | 86 ------------------- .../alternative-mpd-local.json | 26 ------ test/unit/config/karma.unit.conf.cjs | 2 +- 4 files changed, 1 insertion(+), 124 deletions(-) delete mode 100644 .claude/settings.local.json delete mode 100644 CLAUDE.md delete mode 100644 test/functional/config/test-configurations/alternative-mpd-local.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json deleted file mode 100644 index 317ab125fb..0000000000 --- a/.claude/settings.local.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "permissions": { - "allow": [ - "Bash(npm run test:*)", - "Bash(npm test)", - "Bash(npm test:*)" - ], - "deny": [], - "ask": [] - } -} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 5c8b168b9c..0000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,86 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -This is dash.js, a JavaScript implementation for MPEG DASH media playback in browsers using Media Source Extensions (MSE) and Encrypted Media Extensions (EME). The codebase is based on the Dash Industry Forum reference implementation with custom extensions for alternative media presentations. - -## Common Development Commands - -### Build Commands -- `npm run build` - Full build (modern + legacy) -- `npm run build-modern` - Build modern version only -- `npm run build-legacy` - Build legacy version only -- `npm run dev` - Development build with TypeScript compilation and webpack watch mode -- `npm start` - Start webpack dev server - -### Testing -- `npm test` - Run unit tests (Karma) -- `npm run test-functional` - Run functional tests -- `npm run lint` - Run ESLint on source files -- `npm run prebuild` - Clean dist, compile TypeScript, run tests and lint - -### Documentation -- `npm run doc` - Generate JSDoc documentation - -## Architecture Overview - -### Core Structure -The codebase follows a modular architecture with clear separation of concerns: - -- **`src/core/`** - Foundation classes (EventBus, FactoryMaker, Logger, Utils) -- **`src/dash/`** - DASH-specific implementations (parsers, controllers, value objects) -- **`src/streaming/`** - Media streaming logic (MediaPlayer, controllers, models) -- **`src/mss/`** - Microsoft Smooth Streaming support -- **`src/offline/`** - Offline playback capabilities - -### Key Components - -#### MediaPlayer (`src/streaming/MediaPlayer.js`) -The main facade providing the public API. Coordinates all other components and manages the overall player lifecycle. - -#### MediaManager (`src/streaming/MediaManager.js`) -Handles alternative media presentations and switching between main and alternative content. Manages video element lifecycle and prebuffering. - -#### Controllers -- **PlaybackController** - Controls playback state and seeking -- **AbrController** - Adaptive bitrate logic -- **StreamController** - Stream management and switching -- **BufferController** - Buffer management per stream type -- **AlternativeMediaController** - Alternative content switching logic - -#### Models -- **VideoModel** - Video element abstraction -- **ManifestModel** - Manifest data management -- **MediaPlayerModel** - Player configuration and settings - -### Factory Pattern -The codebase extensively uses the FactoryMaker pattern for dependency injection and singleton management. Most components are registered as factories using `FactoryMaker.getSingletonFactory()`. - -### Event System -Built around a centralized EventBus system with strongly-typed events defined in `Events.js`. Components communicate through events rather than direct coupling. - -### Alternative Media Presentations -This fork includes custom functionality for switching between main and alternative content streams: -- MediaManager handles video element switching -- AlternativeMediaController manages the switching logic -- Supports prebuffering of alternative content for seamless transitions - -## Development Notes - -### TypeScript -The project uses TypeScript for development with type definitions in `index.d.ts`. Run `tsc` to compile before building. - -### Testing Framework -- **Unit Tests**: Karma + Mocha + Chai + Sinon -- **Functional Tests**: Karma with browser automation -- Tests are located in `test/unit/` and `test/functional/` - -### Build System -Uses Webpack with separate configurations for modern and legacy builds in `build/webpack/`. - -### Code Style -- ESLint configuration enforces coding standards -- JSDoc comments for API documentation -- Factory pattern for component instantiation \ No newline at end of file diff --git a/test/functional/config/test-configurations/alternative-mpd-local.json b/test/functional/config/test-configurations/alternative-mpd-local.json deleted file mode 100644 index 71ad50d968..0000000000 --- a/test/functional/config/test-configurations/alternative-mpd-local.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "browsers": [ - "chrome_custom" - ], - "hostname": "localhost", - "port": 4200, - "protocol": "http", - "concurrency": 1, - "customLaunchers": { - "chrome_custom": { - "base": "Chrome", - "flags": [ - "--disable-web-security", - "--autoplay-policy=no-user-gesture-required", - "--disable-popup-blocking", - "--disable-search-engine-choice-screen", - "--allow-running-insecure-content", - "--disable-features=VizDisplayCompositor" - ] - } - }, - "reporters": [ - "junit", - "mocha" - ] -} \ No newline at end of file diff --git a/test/unit/config/karma.unit.conf.cjs b/test/unit/config/karma.unit.conf.cjs index b5ec68991f..0fb897139a 100644 --- a/test/unit/config/karma.unit.conf.cjs +++ b/test/unit/config/karma.unit.conf.cjs @@ -110,7 +110,7 @@ module.exports = function (config) { // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['ChromeHeadless'], + browsers: ['ChromeHeadless', 'FirefoxHeadless'], // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits