media-groups.test.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749
  1. import QUnit from 'qunit';
  2. import {
  3. useFakeEnvironment
  4. } from './test-helpers.js';
  5. import * as MediaGroups from '../src/media-groups';
  6. QUnit.module('MediaGroups', {
  7. beforeEach(assert) {
  8. this.env = useFakeEnvironment(assert);
  9. this.clock = this.env.clock;
  10. this.requests = this.env.requests;
  11. },
  12. afterEach(assert) {
  13. this.env.restore();
  14. }
  15. });
  16. QUnit.test('createMediaTypes creates skeleton object for all supported media groups',
  17. function(assert) {
  18. const noopToString = 'function noop() {}';
  19. const result = MediaGroups.createMediaTypes();
  20. assert.ok(result.AUDIO, 'created AUDIO media group object');
  21. assert.deepEqual(result.AUDIO.groups, {},
  22. 'created empty object for AUDIO groups');
  23. assert.deepEqual(result.AUDIO.tracks, {},
  24. 'created empty object for AUDIO tracks');
  25. assert.equal(result.AUDIO.activePlaylistLoader, null,
  26. 'AUDIO activePlaylistLoader is null');
  27. assert.equal(result.AUDIO.activeGroup.toString(), noopToString,
  28. 'created noop function for AUDIO activeGroup');
  29. assert.equal(result.AUDIO.activeTrack.toString(), noopToString,
  30. 'created noop function for AUDIO activeTrack');
  31. assert.equal(result.AUDIO.onGroupChanged.toString(), noopToString,
  32. 'created noop function for AUDIO onGroupChanged');
  33. assert.equal(result.AUDIO.onTrackChanged.toString(), noopToString,
  34. 'created noop function for AUDIO onTrackChanged');
  35. assert.ok(result.SUBTITLES, 'created SUBTITLES media group object');
  36. assert.deepEqual(result.SUBTITLES.groups, {},
  37. 'created empty object for SUBTITLES groups');
  38. assert.deepEqual(result.SUBTITLES.tracks, {},
  39. 'created empty object for SUBTITLES tracks');
  40. assert.equal(result.SUBTITLES.activePlaylistLoader, null,
  41. 'SUBTITLES activePlaylistLoader is null');
  42. assert.equal(result.SUBTITLES.activeGroup.toString(), noopToString,
  43. 'created noop function for SUBTITLES activeGroup');
  44. assert.equal(result.SUBTITLES.activeTrack.toString(), noopToString,
  45. 'created noop function for SUBTITLES activeTrack');
  46. assert.equal(result.SUBTITLES.onGroupChanged.toString(), noopToString,
  47. 'created noop function for SUBTITLES onGroupChanged');
  48. assert.equal(result.SUBTITLES.onTrackChanged.toString(), noopToString,
  49. 'created noop function for SUBTITLES onTrackChanged');
  50. assert.ok(result['CLOSED-CAPTIONS'], 'created CLOSED-CAPTIONS media group object');
  51. assert.deepEqual(result['CLOSED-CAPTIONS'].groups, {},
  52. 'created empty object for CLOSED-CAPTIONS groups');
  53. assert.deepEqual(result['CLOSED-CAPTIONS'].tracks, {},
  54. 'created empty object for CLOSED-CAPTIONS tracks');
  55. assert.equal(result['CLOSED-CAPTIONS'].activePlaylistLoader, null,
  56. 'CLOSED-CAPTIONS activePlaylistLoader is null');
  57. assert.equal(result['CLOSED-CAPTIONS'].activeGroup.toString(), noopToString,
  58. 'created noop function for CLOSED-CAPTIONS activeGroup');
  59. assert.equal(result['CLOSED-CAPTIONS'].activeTrack.toString(), noopToString,
  60. 'created noop function for CLOSED-CAPTIONS activeTrack');
  61. assert.equal(result['CLOSED-CAPTIONS'].onGroupChanged.toString(), noopToString,
  62. 'created noop function for CLOSED-CAPTIONS onGroupChanged');
  63. assert.equal(result['CLOSED-CAPTIONS'].onTrackChanged.toString(), noopToString,
  64. 'created noop function for CLOSED-CAPTIONS onTrackChanged');
  65. });
  66. QUnit.test('stopLoaders pauses segment loader and playlist loader when available',
  67. function(assert) {
  68. let segmentLoaderAbortCalls = 0;
  69. let segmentLoaderPauseCalls = 0;
  70. let playlistLoaderPauseCalls = 0;
  71. const segmentLoader = {
  72. abort: () => segmentLoaderAbortCalls++,
  73. pause: () => segmentLoaderPauseCalls++
  74. };
  75. const playlistLoader = {
  76. pause: () => playlistLoaderPauseCalls++
  77. };
  78. const mediaType = { activePlaylistLoader: null };
  79. MediaGroups.stopLoaders(segmentLoader, mediaType);
  80. assert.equal(segmentLoaderAbortCalls, 1, 'aborted segment loader');
  81. assert.equal(segmentLoaderPauseCalls, 1, 'paused segment loader');
  82. assert.equal(playlistLoaderPauseCalls, 0, 'no pause when no active playlist loader');
  83. mediaType.activePlaylistLoader = playlistLoader;
  84. MediaGroups.stopLoaders(segmentLoader, mediaType);
  85. assert.equal(segmentLoaderAbortCalls, 2, 'aborted segment loader');
  86. assert.equal(segmentLoaderPauseCalls, 2, 'paused segment loader');
  87. assert.equal(playlistLoaderPauseCalls, 1, 'pause active playlist loader');
  88. assert.equal(mediaType.activePlaylistLoader, null,
  89. 'clears active playlist loader for media group');
  90. });
  91. QUnit.test('startLoaders starts playlist loader when appropriate',
  92. function(assert) {
  93. let playlistLoaderLoadCalls = 0;
  94. let media = null;
  95. const playlistLoader = {
  96. load: () => playlistLoaderLoadCalls++,
  97. media: () => media
  98. };
  99. const mediaType = { activePlaylistLoader: null };
  100. MediaGroups.startLoaders(playlistLoader, mediaType);
  101. assert.equal(playlistLoaderLoadCalls, 1, 'called load on playlist loader');
  102. assert.strictEqual(mediaType.activePlaylistLoader, playlistLoader,
  103. 'set active playlist loader for media group');
  104. });
  105. QUnit.test('activeTrack returns the correct audio track', function(assert) {
  106. const type = 'AUDIO';
  107. const settings = { mediaTypes: MediaGroups.createMediaTypes() };
  108. const tracks = settings.mediaTypes[type].tracks;
  109. const activeTrack = MediaGroups.activeTrack[type](type, settings);
  110. assert.equal(activeTrack(), null, 'returns null when empty track list');
  111. tracks.track1 = { id: 'track1', enabled: false };
  112. tracks.track2 = { id: 'track2', enabled: false };
  113. tracks.track3 = { id: 'track3', enabled: false };
  114. assert.equal(activeTrack(), null, 'returns null when no active tracks');
  115. tracks.track3.enabled = true;
  116. assert.strictEqual(activeTrack(), tracks.track3, 'returns active track');
  117. tracks.track1.enabled = true;
  118. // video.js treats the first enabled track in the track list as the active track
  119. // so we want the same behavior here
  120. assert.strictEqual(activeTrack(), tracks.track1, 'returns first active track');
  121. tracks.track1.enabled = false;
  122. assert.strictEqual(activeTrack(), tracks.track3, 'returns active track');
  123. tracks.track3.enabled = false;
  124. assert.equal(activeTrack(), null, 'returns null when no active tracks');
  125. });
  126. QUnit.test('activeTrack returns the correct subtitle track', function(assert) {
  127. const type = 'SUBTITLES';
  128. const settings = { mediaTypes: MediaGroups.createMediaTypes() };
  129. const tracks = settings.mediaTypes[type].tracks;
  130. const activeTrack = MediaGroups.activeTrack[type](type, settings);
  131. assert.equal(activeTrack(), null, 'returns null when empty track list');
  132. tracks.track1 = { id: 'track1', mode: 'disabled' };
  133. tracks.track2 = { id: 'track2', mode: 'hidden' };
  134. tracks.track3 = { id: 'track3', mode: 'disabled' };
  135. assert.equal(activeTrack(), null, 'returns null when no active tracks');
  136. tracks.track3.mode = 'showing';
  137. assert.strictEqual(activeTrack(), tracks.track3, 'returns active track');
  138. tracks.track1.mode = 'showing';
  139. // video.js treats the first enabled track in the track list as the active track
  140. // so we want the same behavior here
  141. assert.strictEqual(activeTrack(), tracks.track1, 'returns first active track');
  142. tracks.track1.mode = 'disabled';
  143. assert.strictEqual(activeTrack(), tracks.track3, 'returns active track');
  144. tracks.track3.mode = 'hidden';
  145. assert.equal(activeTrack(), null, 'returns null when no active tracks');
  146. });
  147. QUnit.test('activeGroup returns the correct audio group', function(assert) {
  148. const type = 'AUDIO';
  149. let media = null;
  150. const settings = {
  151. mediaTypes: MediaGroups.createMediaTypes(),
  152. masterPlaylistLoader: {
  153. media: () => media
  154. }
  155. };
  156. const groups = settings.mediaTypes[type].groups;
  157. const tracks = settings.mediaTypes[type].tracks;
  158. const activeTrack = MediaGroups.activeTrack[type](type, settings);
  159. const activeGroup = MediaGroups.activeGroup(type, settings);
  160. assert.equal(activeGroup(), null, 'returns null when no media in masterPlaylistLoader');
  161. media = { attributes: { } };
  162. groups.main = [{ id: 'en' }, { id: 'fr' }];
  163. assert.strictEqual(activeGroup(), groups.main,
  164. 'defaults to main audio group when media does not specify audio group');
  165. groups.audio = [{ id: 'en'}, { id: 'fr' }];
  166. media.attributes.AUDIO = 'audio';
  167. assert.strictEqual(activeGroup(), groups.audio,
  168. 'returns list of variants in active audio group');
  169. tracks.en = { id: 'en', enabled: false };
  170. tracks.fr = { id: 'fr', enabled: false };
  171. assert.equal(activeGroup(activeTrack()), null,
  172. 'returns null when an active track is specified, but there is no active track');
  173. tracks.fr.enabled = true;
  174. assert.strictEqual(activeGroup(activeTrack()), groups.audio[1],
  175. 'returned the active group corresponding to the active track');
  176. });
  177. QUnit.test('activeGroup returns the correct subtitle group', function(assert) {
  178. const type = 'SUBTITLES';
  179. let media = null;
  180. const settings = {
  181. mediaTypes: MediaGroups.createMediaTypes(),
  182. masterPlaylistLoader: {
  183. media: () => media
  184. }
  185. };
  186. const groups = settings.mediaTypes[type].groups;
  187. const tracks = settings.mediaTypes[type].tracks;
  188. const activeTrack = MediaGroups.activeTrack[type](type, settings);
  189. const activeGroup = MediaGroups.activeGroup(type, settings);
  190. assert.equal(activeGroup(), null, 'returns null when no media in masterPlaylistLoader');
  191. media = { attributes: { } };
  192. // there is no default `main` group for subtitles like there is for audio
  193. assert.notOk(activeGroup(), 'returns null when media does not specify subtitle group');
  194. groups.subs = [{ id: 'en'}, { id: 'fr' }];
  195. media.attributes.SUBTITLES = 'subs';
  196. assert.strictEqual(activeGroup(), groups.subs,
  197. 'returns list of variants in active subtitle group');
  198. tracks.en = { id: 'en', mode: 'disabled' };
  199. tracks.fr = { id: 'fr', mode: 'disabled' };
  200. assert.equal(activeGroup(activeTrack()), null,
  201. 'returns null when an active track is specified, but there is no active track');
  202. tracks.fr.mode = 'showing';
  203. assert.strictEqual(activeGroup(activeTrack()), groups.subs[1],
  204. 'returned the active group corresponding to the active track');
  205. });
  206. QUnit.test('onGroupChanged updates active playlist loader and resyncs segment loader',
  207. function(assert) {
  208. let mainSegmentLoaderResetCalls = 0;
  209. let segmentLoaderResyncCalls = 0;
  210. let segmentLoaderPauseCalls = 0;
  211. const type = 'AUDIO';
  212. const media = { attributes: { AUDIO: 'main' } };
  213. const mainSegmentLoader = { resetEverything: () => mainSegmentLoaderResetCalls++ };
  214. const segmentLoader = {
  215. abort() {},
  216. pause: () => segmentLoaderPauseCalls++,
  217. load() {},
  218. playlist() {},
  219. resyncLoader: () => segmentLoaderResyncCalls++
  220. };
  221. const mockPlaylistLoader = () => {
  222. return {
  223. media: () => media,
  224. load() {},
  225. pause() {}
  226. };
  227. };
  228. const masterPlaylistLoader = mockPlaylistLoader();
  229. const settings = {
  230. segmentLoaders: {
  231. AUDIO: segmentLoader,
  232. main: mainSegmentLoader
  233. },
  234. mediaTypes: MediaGroups.createMediaTypes(),
  235. masterPlaylistLoader
  236. };
  237. const mediaType = settings.mediaTypes[type];
  238. const groups = mediaType.groups;
  239. const tracks = mediaType.tracks;
  240. groups.main = [
  241. { id: 'en', playlistLoader: null },
  242. { id: 'fr', playlistLoader: mockPlaylistLoader() },
  243. { id: 'es', playlistLoader: mockPlaylistLoader() }
  244. ];
  245. tracks.en = { id: 'en', enabled: false };
  246. tracks.fr = { id: 'fr', enabled: false };
  247. tracks.es = { id: 'es', enabled: false };
  248. mediaType.activeTrack = MediaGroups.activeTrack[type](type, settings);
  249. mediaType.activeGroup = MediaGroups.activeGroup(type, settings);
  250. const onGroupChanged = MediaGroups.onGroupChanged(type, settings);
  251. onGroupChanged();
  252. assert.equal(segmentLoaderPauseCalls, 1, 'loaders paused on group change');
  253. assert.equal(mainSegmentLoaderResetCalls, 0, 'no reset when no active group');
  254. assert.equal(segmentLoaderResyncCalls, 0, 'no resync when no active group');
  255. tracks.en.enabled = true;
  256. onGroupChanged();
  257. assert.equal(segmentLoaderPauseCalls, 2, 'loaders paused on group change');
  258. assert.equal(mainSegmentLoaderResetCalls, 0,
  259. 'no reset changing from no active playlist loader to group with no playlist loader');
  260. assert.equal(segmentLoaderResyncCalls, 0,
  261. 'no resync changing to group with no playlist loader');
  262. mediaType.activePlaylistLoader = groups.main[1].playlistLoader;
  263. onGroupChanged();
  264. assert.equal(segmentLoaderPauseCalls, 3, 'loaders paused on group change');
  265. assert.equal(mainSegmentLoaderResetCalls, 1,
  266. 'reset changing from active playlist loader to group with no playlist loader');
  267. assert.equal(segmentLoaderResyncCalls, 0,
  268. 'no resync changing to group with no playlist loader');
  269. tracks.en.enabled = false;
  270. tracks.fr.enabled = true;
  271. mediaType.activePlaylistLoader = groups.main[2].playlistLoader;
  272. onGroupChanged();
  273. assert.equal(segmentLoaderPauseCalls, 4, 'loaders paused on group change');
  274. assert.equal(mainSegmentLoaderResetCalls, 1,
  275. 'no reset changing to group with playlist loader');
  276. assert.equal(segmentLoaderResyncCalls, 1,
  277. 'resync changing to group with playlist loader');
  278. assert.strictEqual(mediaType.activePlaylistLoader, groups.main[1].playlistLoader,
  279. 'sets the correct active playlist loader');
  280. });
  281. QUnit.test('onTrackChanged updates active playlist loader and resets segment loader',
  282. function(assert) {
  283. let mainSegmentLoaderResetCalls = 0;
  284. let segmentLoaderResetCalls = 0;
  285. let segmentLoaderPauseCalls = 0;
  286. let segmentLoaderTrack;
  287. const type = 'AUDIO';
  288. const media = { attributes: { AUDIO: 'main' } };
  289. const mainSegmentLoader = { resetEverything: () => mainSegmentLoaderResetCalls++ };
  290. const segmentLoader = {
  291. abort() {},
  292. pause: () => segmentLoaderPauseCalls++,
  293. playlist() {},
  294. resetEverything: () => segmentLoaderResetCalls++
  295. };
  296. const mockPlaylistLoader = () => {
  297. return {
  298. media: () => media,
  299. load() {},
  300. pause() {}
  301. };
  302. };
  303. const masterPlaylistLoader = mockPlaylistLoader();
  304. const settings = {
  305. segmentLoaders: {
  306. AUDIO: segmentLoader,
  307. main: mainSegmentLoader
  308. },
  309. mediaTypes: MediaGroups.createMediaTypes(),
  310. masterPlaylistLoader
  311. };
  312. const mediaType = settings.mediaTypes[type];
  313. const groups = mediaType.groups;
  314. const tracks = mediaType.tracks;
  315. groups.main = [
  316. { id: 'en', playlistLoader: null },
  317. { id: 'fr', playlistLoader: mockPlaylistLoader() },
  318. { id: 'es', playlistLoader: mockPlaylistLoader() }
  319. ];
  320. tracks.en = { id: 'en', enabled: false };
  321. tracks.fr = { id: 'fr', enabled: false };
  322. tracks.es = { id: 'es', enabled: false };
  323. mediaType.activeTrack = MediaGroups.activeTrack[type](type, settings);
  324. mediaType.activeGroup = MediaGroups.activeGroup(type, settings);
  325. const onTrackChanged = MediaGroups.onTrackChanged(type, settings);
  326. onTrackChanged();
  327. assert.equal(segmentLoaderPauseCalls, 1, 'loaders paused on track change');
  328. assert.equal(mainSegmentLoaderResetCalls, 0, 'no main reset when no active group');
  329. assert.equal(segmentLoaderResetCalls, 0, 'no reset when no active group');
  330. tracks.en.enabled = true;
  331. onTrackChanged();
  332. assert.equal(segmentLoaderPauseCalls, 2, 'loaders paused on track change');
  333. assert.equal(mainSegmentLoaderResetCalls, 1,
  334. 'main reset changing to group with no playlist loader');
  335. assert.equal(segmentLoaderResetCalls, 0,
  336. 'no reset changing to group with no playlist loader');
  337. tracks.en.enabled = false;
  338. tracks.fr.enabled = true;
  339. mediaType.activePlaylistLoader = groups.main[1].playlistLoader;
  340. onTrackChanged();
  341. assert.equal(segmentLoaderPauseCalls, 3, 'loaders paused on track change');
  342. assert.equal(mainSegmentLoaderResetCalls, 1,
  343. 'no main reset changing to group with playlist loader');
  344. assert.equal(segmentLoaderResetCalls, 0,
  345. 'no reset when active group hasn\'t changed');
  346. assert.strictEqual(mediaType.activePlaylistLoader, groups.main[1].playlistLoader,
  347. 'sets the correct active playlist loader');
  348. mediaType.activePlaylistLoader = groups.main[2].playlistLoader;
  349. onTrackChanged();
  350. assert.equal(segmentLoaderPauseCalls, 4, 'loaders paused on track change');
  351. assert.equal(mainSegmentLoaderResetCalls, 1,
  352. 'no main reset changing to group with playlist loader');
  353. assert.equal(segmentLoaderResetCalls, 1,
  354. 'reset on track change');
  355. assert.strictEqual(mediaType.activePlaylistLoader, groups.main[1].playlistLoader,
  356. 'sets the correct active playlist loader');
  357. // setting the track on the segment loader only applies to the SUBTITLES case.
  358. // even though this test is testing type AUDIO, aside from this difference of setting
  359. // the track, the functionality between the types is the same.
  360. segmentLoader.track = (track) => segmentLoaderTrack = track;
  361. mediaType.activePlaylistLoader = groups.main[2].playlistLoader;
  362. onTrackChanged();
  363. assert.equal(segmentLoaderPauseCalls, 5, 'loaders paused on track change');
  364. assert.equal(mainSegmentLoaderResetCalls, 1,
  365. 'no main reset changing to group with playlist loader');
  366. assert.equal(segmentLoaderResetCalls, 2,
  367. 'reset on track change');
  368. assert.strictEqual(mediaType.activePlaylistLoader, groups.main[1].playlistLoader,
  369. 'sets the correct active playlist loader');
  370. assert.strictEqual(segmentLoaderTrack, tracks.fr,
  371. 'set the correct track on the segment loader');
  372. });
  373. QUnit.test('switches to default audio track when an error is encountered',
  374. function(assert) {
  375. let blacklistCurrentPlaylistCalls = 0;
  376. let onTrackChangedCalls = 0;
  377. const type = 'AUDIO';
  378. const segmentLoader = { abort() {}, pause() {} };
  379. const masterPlaylistLoader = {
  380. media() {
  381. return { attributes: { AUDIO: 'main' } };
  382. }
  383. };
  384. const settings = {
  385. segmentLoaders: { AUDIO: segmentLoader },
  386. mediaTypes: MediaGroups.createMediaTypes(),
  387. blacklistCurrentPlaylist: () => blacklistCurrentPlaylistCalls++,
  388. masterPlaylistLoader
  389. };
  390. const mediaType = settings.mediaTypes[type];
  391. const groups = mediaType.groups;
  392. const tracks = mediaType.tracks;
  393. mediaType.activeTrack = MediaGroups.activeTrack[type](type, settings);
  394. mediaType.activeGroup = MediaGroups.activeGroup(type, settings);
  395. mediaType.onTrackChanged = () => onTrackChangedCalls++;
  396. const onError = MediaGroups.onError[type](type, settings);
  397. groups.main = [ { id: 'en', default: true }, { id: 'fr'}, { id: 'es'} ];
  398. tracks.en = { id: 'en', enabed: false };
  399. tracks.fr = { id: 'fr', enabed: true };
  400. tracks.es = { id: 'es', enabed: false };
  401. onError();
  402. assert.equal(blacklistCurrentPlaylistCalls, 0, 'did not blacklist current playlist');
  403. assert.equal(onTrackChangedCalls, 1, 'called onTrackChanged after changing to default');
  404. assert.equal(tracks.en.enabled, true, 'enabled default track');
  405. assert.equal(tracks.fr.enabled, false, 'disabled active track');
  406. assert.equal(tracks.es.enabled, false, 'disabled track still disabled');
  407. assert.equal(this.env.log.warn.callCount, 1, 'logged a warning');
  408. this.env.log.warn.callCount = 0;
  409. onError();
  410. assert.equal(blacklistCurrentPlaylistCalls, 1, 'blacklist current playlist');
  411. assert.equal(onTrackChangedCalls, 1, 'did not call onTrackChanged after blacklist');
  412. assert.equal(tracks.en.enabled, true, 'default track still enabled');
  413. assert.equal(tracks.fr.enabled, false, 'disabled track still disabled');
  414. assert.equal(tracks.es.enabled, false, 'disabled track still disabled');
  415. assert.equal(this.env.log.warn.callCount, 0, 'no warning logged');
  416. });
  417. QUnit.test('disables subtitle track when an error is encountered', function(assert) {
  418. let onTrackChangedCalls = 0;
  419. const type = 'SUBTITLES';
  420. const segmentLoader = { abort() {}, pause() {} };
  421. const settings = {
  422. segmentLoaders: { SUBTITLES: segmentLoader },
  423. mediaTypes: MediaGroups.createMediaTypes()
  424. };
  425. const mediaType = settings.mediaTypes[type];
  426. const tracks = mediaType.tracks;
  427. mediaType.activeTrack = MediaGroups.activeTrack[type](type, settings);
  428. mediaType.onTrackChanged = () => onTrackChangedCalls++;
  429. const onError = MediaGroups.onError[type](type, settings);
  430. tracks.en = { id: 'en', mode: 'disabled' };
  431. tracks.fr = { id: 'fr', mode: 'disabled' };
  432. tracks.es = { id: 'es', mode: 'showing' };
  433. onError();
  434. assert.equal(onTrackChangedCalls, 1, 'called onTrackChanged after disabling track');
  435. assert.equal(tracks.en.mode, 'disabled', 'disabled track still disabled');
  436. assert.equal(tracks.fr.mode, 'disabled', 'disabled track still disabled');
  437. assert.equal(tracks.es.mode, 'disabled', 'disabled active track');
  438. assert.equal(this.env.log.warn.callCount, 1, 'logged a warning');
  439. this.env.log.warn.callCount = 0;
  440. });
  441. QUnit.test('setupListeners adds correct playlist loader listeners', function(assert) {
  442. const settings = {
  443. tech: {},
  444. requestOptions: {},
  445. segmentLoaders: {
  446. AUDIO: {},
  447. SUBTITLES: {}
  448. },
  449. mediaTypes: MediaGroups.createMediaTypes()
  450. };
  451. const listeners = [];
  452. const on = (event, cb) => listeners.push([event, cb]);
  453. const playlistLoader = { on };
  454. let type = 'SUBTITLES';
  455. MediaGroups.setupListeners[type](type, playlistLoader, settings);
  456. assert.equal(listeners.length, 3, 'setup 3 event listeners');
  457. assert.equal(listeners[0][0], 'loadedmetadata', 'setup loadedmetadata listener');
  458. assert.equal(typeof listeners[0][1], 'function', 'setup loadedmetadata listener');
  459. assert.equal(listeners[1][0], 'loadedplaylist', 'setup loadedmetadata listener');
  460. assert.equal(typeof listeners[1][1], 'function', 'setup loadedmetadata listener');
  461. assert.equal(listeners[2][0], 'error', 'setup loadedmetadata listener');
  462. assert.equal(typeof listeners[2][1], 'function', 'setup loadedmetadata listener');
  463. listeners.length = 0;
  464. type = 'AUDIO';
  465. MediaGroups.setupListeners[type](type, playlistLoader, settings);
  466. assert.equal(listeners.length, 3, 'setup 3 event listeners');
  467. assert.equal(listeners[0][0], 'loadedmetadata', 'setup loadedmetadata listener');
  468. assert.equal(typeof listeners[0][1], 'function', 'setup loadedmetadata listener');
  469. assert.equal(listeners[1][0], 'loadedplaylist', 'setup loadedmetadata listener');
  470. assert.equal(typeof listeners[1][1], 'function', 'setup loadedmetadata listener');
  471. assert.equal(listeners[2][0], 'error', 'setup loadedmetadata listener');
  472. assert.equal(typeof listeners[2][1], 'function', 'setup loadedmetadata listener');
  473. listeners.length = 0;
  474. MediaGroups.setupListeners[type](type, null, settings);
  475. assert.equal(listeners.length, 0, 'no event listeners setup when no playlist loader');
  476. });
  477. QUnit.module('MediaGroups - initialize', {
  478. beforeEach(assert) {
  479. this.mediaTypes = MediaGroups.createMediaTypes();
  480. this.master = {
  481. mediaGroups: {
  482. 'AUDIO': {},
  483. 'SUBTITLES': {},
  484. 'CLOSED-CAPTIONS': {}
  485. }
  486. };
  487. this.settings = {
  488. mode: 'html5',
  489. hls: {},
  490. tech: {
  491. addRemoteTextTrack(track) {
  492. return { track };
  493. }
  494. },
  495. segmentLoaders: {
  496. AUDIO: { on() {} },
  497. SUBTITLES: { on() {} }
  498. },
  499. requestOptions: { withCredentials: false, timeout: 10 },
  500. master: this.master,
  501. mediaTypes: this.mediaTypes,
  502. blacklistCurrentPlaylist() {}
  503. };
  504. }
  505. });
  506. QUnit.test('initialize audio forces default track when no audio groups provided',
  507. function(assert) {
  508. const type = 'AUDIO';
  509. MediaGroups.initialize[type](type, this.settings);
  510. assert.deepEqual(this.master.mediaGroups[type],
  511. { main: { default: { default: true } } }, 'forced default audio group');
  512. assert.deepEqual(this.mediaTypes[type].groups,
  513. { main: [ { id: 'default', playlistLoader: null, default: true } ] },
  514. 'creates group properties and no playlist loader');
  515. assert.ok(this.mediaTypes[type].tracks.default, 'created default track');
  516. });
  517. QUnit.test('initialize audio correctly generates tracks and playlist loaders',
  518. function(assert) {
  519. const type = 'AUDIO';
  520. this.master.mediaGroups[type].aud1 = {
  521. en: { default: true, language: 'en' },
  522. fr: { default: false, language: 'fr', resolvedUri: 'aud1/fr.m3u8' }
  523. };
  524. this.master.mediaGroups[type].aud2 = {
  525. en: { default: true, language: 'en' },
  526. fr: { default: false, language: 'fr', resolvedUri: 'aud2/fr.m3u8' }
  527. };
  528. MediaGroups.initialize[type](type, this.settings);
  529. assert.notOk(this.master.mediaGroups[type].main, 'no default main group added');
  530. assert.deepEqual(this.mediaTypes[type].groups,
  531. {
  532. aud1: [
  533. { id: 'en', default: true, language: 'en', playlistLoader: null },
  534. { id: 'fr', default: false, language: 'fr', resolvedUri: 'aud1/fr.m3u8',
  535. // just so deepEqual passes since there is no other way to get the object
  536. // reference for the playlist loader. Assertions below will confirm that this is
  537. // not null.
  538. playlistLoader: this.mediaTypes[type].groups.aud1[1].playlistLoader }
  539. ],
  540. aud2: [
  541. { id: 'en', default: true, language: 'en', playlistLoader: null },
  542. { id: 'fr', default: false, language: 'fr', resolvedUri: 'aud2/fr.m3u8',
  543. // just so deepEqual passes since there is no other way to get the object
  544. // reference for the playlist loader. Assertions below will confirm that this is
  545. // not null.
  546. playlistLoader: this.mediaTypes[type].groups.aud2[1].playlistLoader }
  547. ]
  548. }, 'creates group properties');
  549. assert.ok(this.mediaTypes[type].groups.aud1[1].playlistLoader,
  550. 'playlistLoader created for non muxed audio group');
  551. assert.ok(this.mediaTypes[type].groups.aud2[1].playlistLoader,
  552. 'playlistLoader created for non muxed audio group');
  553. assert.ok(this.mediaTypes[type].tracks.en, 'created audio track');
  554. assert.ok(this.mediaTypes[type].tracks.fr, 'created audio track');
  555. });
  556. QUnit.test('initialize subtitles correctly generates tracks and playlist loaders',
  557. function(assert) {
  558. const type = 'SUBTITLES';
  559. this.master.mediaGroups[type].sub1 = {
  560. 'en': { language: 'en', resolvedUri: 'sub1/en.m3u8' },
  561. 'en-forced': { language: 'en', resolvedUri: 'sub1/en-forced.m3u8', forced: true },
  562. 'fr': { language: 'fr', resolvedUri: 'sub1/fr.m3u8' }
  563. };
  564. this.master.mediaGroups[type].sub2 = {
  565. 'en': { language: 'en', resolvedUri: 'sub2/en.m3u8' },
  566. 'en-forced': { language: 'en', resolvedUri: 'sub2/en-forced.m3u8', forced: true },
  567. 'fr': { language: 'fr', resolvedUri: 'sub2/fr.m3u8' }
  568. };
  569. MediaGroups.initialize[type](type, this.settings);
  570. assert.deepEqual(this.mediaTypes[type].groups,
  571. {
  572. sub1: [
  573. { id: 'en', language: 'en', resolvedUri: 'sub1/en.m3u8',
  574. playlistLoader: this.mediaTypes[type].groups.sub1[0].playlistLoader },
  575. { id: 'fr', language: 'fr', resolvedUri: 'sub1/fr.m3u8',
  576. playlistLoader: this.mediaTypes[type].groups.sub1[1].playlistLoader }
  577. ],
  578. sub2: [
  579. { id: 'en', language: 'en', resolvedUri: 'sub2/en.m3u8',
  580. playlistLoader: this.mediaTypes[type].groups.sub2[0].playlistLoader },
  581. { id: 'fr', language: 'fr', resolvedUri: 'sub2/fr.m3u8',
  582. playlistLoader: this.mediaTypes[type].groups.sub2[1].playlistLoader }
  583. ]
  584. }, 'creates group properties');
  585. assert.ok(this.mediaTypes[type].groups.sub1[0].playlistLoader,
  586. 'playlistLoader created');
  587. assert.ok(this.mediaTypes[type].groups.sub1[1].playlistLoader,
  588. 'playlistLoader created');
  589. assert.ok(this.mediaTypes[type].groups.sub2[0].playlistLoader,
  590. 'playlistLoader created');
  591. assert.ok(this.mediaTypes[type].groups.sub2[1].playlistLoader,
  592. 'playlistLoader created');
  593. assert.ok(this.mediaTypes[type].tracks.en, 'created text track');
  594. assert.ok(this.mediaTypes[type].tracks.fr, 'created text track');
  595. });
  596. QUnit.test('initialize closed-captions correctly generates tracks and NO loaders',
  597. function(assert) {
  598. const type = 'CLOSED-CAPTIONS';
  599. this.master.mediaGroups[type].CCs = {
  600. en608: { language: 'en', instreamId: 'CC1' },
  601. en708: { language: 'en', instreamId: 'SERVICE1' },
  602. fr608: { language: 'fr', instreamId: 'CC3' },
  603. fr708: { language: 'fr', instreamId: 'SERVICE3' }
  604. };
  605. MediaGroups.initialize[type](type, this.settings);
  606. assert.deepEqual(this.mediaTypes[type].groups,
  607. {
  608. CCs: [
  609. { id: 'en608', language: 'en', instreamId: 'CC1' },
  610. { id: 'fr608', language: 'fr', instreamId: 'CC3' }
  611. ]
  612. }, 'creates group properties');
  613. assert.ok(this.mediaTypes[type].tracks.en608, 'created text track');
  614. assert.ok(this.mediaTypes[type].tracks.fr608, 'created text track');
  615. });