@gcorevideo/player 2.22.0 → 2.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/assets/bottom-gear/bottomgear copy.ejs +10 -0
  2. package/assets/bottom-gear/bottomgear.ejs +4 -8
  3. package/assets/bottom-gear/gear-sub-menu.scss +0 -1
  4. package/assets/bottom-gear/gear.scss +0 -1
  5. package/assets/clappr-nerd-stats/button.ejs +3 -3
  6. package/assets/level-selector/button.ejs +2 -4
  7. package/assets/level-selector/list.ejs +14 -10
  8. package/assets/level-selector/style.scss +9 -4
  9. package/assets/playback-rate/list.ejs +5 -5
  10. package/dist/core.js +1 -2
  11. package/dist/index.css +1104 -1103
  12. package/dist/index.js +3849 -3767
  13. package/dist/player.d.ts +10 -17
  14. package/dist/plugins/index.css +1541 -1540
  15. package/dist/plugins/index.js +3949 -3868
  16. package/docs/api/player.mediacontrol.md +8 -36
  17. package/docs/api/player.mediacontrol.toggleelement.md +72 -0
  18. package/docs/api/player.mediacontrolelement.md +1 -1
  19. package/lib/playback/dash-playback/DashPlayback.d.ts.map +1 -1
  20. package/lib/playback/dash-playback/DashPlayback.js +0 -1
  21. package/lib/plugins/bottom-gear/BottomGear.d.ts +65 -14
  22. package/lib/plugins/bottom-gear/BottomGear.d.ts.map +1 -1
  23. package/lib/plugins/bottom-gear/BottomGear.js +113 -37
  24. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts +2 -3
  25. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.d.ts.map +1 -1
  26. package/lib/plugins/clappr-nerd-stats/ClapprNerdStats.js +18 -15
  27. package/lib/plugins/dvr-controls/DvrControls.js +1 -1
  28. package/lib/plugins/level-selector/LevelSelector.d.ts +8 -11
  29. package/lib/plugins/level-selector/LevelSelector.d.ts.map +1 -1
  30. package/lib/plugins/level-selector/LevelSelector.js +66 -102
  31. package/lib/plugins/media-control/MediaControl.d.ts +7 -5
  32. package/lib/plugins/media-control/MediaControl.d.ts.map +1 -1
  33. package/lib/plugins/media-control/MediaControl.js +37 -19
  34. package/lib/plugins/picture-in-picture/PictureInPicture.d.ts.map +1 -1
  35. package/lib/plugins/picture-in-picture/PictureInPicture.js +7 -2
  36. package/lib/plugins/playback-rate/PlaybackRate.d.ts +42 -14
  37. package/lib/plugins/playback-rate/PlaybackRate.d.ts.map +1 -1
  38. package/lib/plugins/playback-rate/PlaybackRate.js +101 -83
  39. package/lib/plugins/subtitles/ClosedCaptions.js +1 -1
  40. package/lib/testUtils.d.ts +1 -0
  41. package/lib/testUtils.d.ts.map +1 -1
  42. package/lib/testUtils.js +13 -0
  43. package/package.json +1 -1
  44. package/src/playback/dash-playback/DashPlayback.ts +0 -1
  45. package/src/plugins/bottom-gear/BottomGear.ts +162 -72
  46. package/src/plugins/bottom-gear/__tests__/BottomGear.test.ts +21 -5
  47. package/src/plugins/bottom-gear/__tests__/__snapshots__/BottomGear.test.ts.snap +5 -12
  48. package/src/plugins/clappr-nerd-stats/ClapprNerdStats.ts +27 -25
  49. package/src/plugins/dvr-controls/DvrControls.ts +1 -1
  50. package/src/plugins/dvr-controls/__tests__/DvrControls.test.ts +1 -1
  51. package/src/plugins/level-selector/LevelSelector.ts +80 -120
  52. package/src/plugins/level-selector/__tests__/LevelSelector.test.ts +69 -79
  53. package/src/plugins/level-selector/__tests__/__snapshots__/LevelSelector.test.ts.snap +38 -71
  54. package/src/plugins/media-control/MediaControl.ts +51 -25
  55. package/src/plugins/media-control/__tests__/MediaControl.test.ts +4 -4
  56. package/src/plugins/picture-in-picture/PictureInPicture.ts +7 -2
  57. package/src/plugins/playback-rate/PlaybackRate.ts +136 -108
  58. package/src/plugins/playback-rate/__tests__/PlaybackRate.test.ts +84 -37
  59. package/src/plugins/playback-rate/__tests__/__snapshots__/PlaybackRate.test.ts.snap +55 -6
  60. package/src/plugins/subtitles/ClosedCaptions.ts +1 -1
  61. package/src/plugins/subtitles/__tests__/ClosedCaptions.test.ts +1 -1
  62. package/src/testUtils.ts +14 -0
  63. package/src/typings/vitest.d.ts +1 -0
  64. package/temp/player.api.json +66 -94
  65. package/tsconfig.tsbuildinfo +1 -1
  66. package/docs/api/player.mediacontrol.getcenterpanel.md +0 -18
  67. package/docs/api/player.mediacontrol.getleftpanel.md +0 -22
  68. package/docs/api/player.mediacontrol.getrightpanel.md +0 -22
@@ -6,7 +6,7 @@ import { type QualityLevel } from '../../playback.types.js'
6
6
  import { CLAPPR_VERSION } from '../../build.js'
7
7
  import { ZeptoResult } from '../../types.js'
8
8
  import { TemplateFunction } from '../types.js'
9
- import { BottomGear } from '../bottom-gear/BottomGear.js'
9
+ import { BottomGear, GearEvents } from '../bottom-gear/BottomGear.js'
10
10
 
11
11
  import buttonHtml from '../../../assets/level-selector/button.ejs'
12
12
  import listHtml from '../../../assets/level-selector/list.ejs'
@@ -15,7 +15,7 @@ import arrowRightIcon from '../../../assets/icons/new/arrow-right.svg'
15
15
  import arrowLeftIcon from '../../../assets/icons/new/arrow-left.svg'
16
16
  import checkIcon from '../../../assets/icons/new/check.svg'
17
17
  import '../../../assets/level-selector/style.scss'
18
- import { MediaControl, MediaControlEvents } from '../media-control/MediaControl.js'
18
+ import { MediaControl } from '../media-control/MediaControl.js'
19
19
 
20
20
  const T = 'plugins.level_selector'
21
21
  const VERSION = '2.19.4'
@@ -73,7 +73,9 @@ export class LevelSelector extends UICorePlugin {
73
73
 
74
74
  private isHd = false
75
75
 
76
- private isOpen = false
76
+ private currentText = ''
77
+
78
+ private selectedLevelId = -1
77
79
 
78
80
  private static readonly buttonTemplate: TemplateFunction =
79
81
  template(buttonHtml)
@@ -111,14 +113,9 @@ export class LevelSelector extends UICorePlugin {
111
113
  }
112
114
  }
113
115
 
114
- private currentText = 'Auto'
115
-
116
- private selectedLevelId = -1
117
-
118
116
  override get events() {
119
117
  return {
120
- 'click .gear-sub-menu_btn': 'onLevelSelect',
121
- 'click .gear-option': 'onShowLevelSelectMenu',
118
+ 'click .gear-sub-menu_btn': 'onSelect',
122
119
  'click .go-back': 'goBack',
123
120
  }
124
121
  }
@@ -127,27 +124,29 @@ export class LevelSelector extends UICorePlugin {
127
124
  * @internal
128
125
  */
129
126
  override bindEvents() {
130
- this.listenTo(this.core, Events.CORE_READY, this.onCoreReady);
127
+ this.listenToOnce(this.core, Events.CORE_READY, this.onCoreReady)
131
128
  this.listenTo(
132
129
  this.core,
133
130
  Events.CORE_ACTIVE_CONTAINER_CHANGED,
134
- this.bindPlaybackEvents,
131
+ this.onActiveContainerChange,
135
132
  )
136
133
  }
137
134
 
138
135
  private onCoreReady() {
139
- trace(`${T} onCoreReady`);
140
- const mediaControl = this.core.getPlugin('media_control') as MediaControl
141
- assert(mediaControl, 'media_control plugin is required')
142
- this.listenTo(mediaControl, MediaControlEvents.MEDIACONTROL_GEAR_RENDERED, this.onGearRendered);
136
+ trace(`${T} onCoreReady`)
137
+ const gear = this.core.getPlugin('bottom_gear') as BottomGear
138
+ assert(gear, 'bottom_gear plugin is required')
139
+
140
+ this.currentText = this.core.i18n.t('auto')
141
+ this.listenTo(gear, GearEvents.RENDERED, this.onGearRendered)
143
142
  }
144
143
 
145
144
  private onGearRendered() {
146
- trace(`${T} onGearRendered`);
147
- this.deferRender();
145
+ trace(`${T} onGearRendered`)
146
+ this.render()
148
147
  }
149
148
 
150
- private bindPlaybackEvents() {
149
+ private onActiveContainerChange() {
151
150
  this.removeAuto = false
152
151
  this.isHd = false
153
152
 
@@ -156,7 +155,7 @@ export class LevelSelector extends UICorePlugin {
156
155
  this.listenTo(
157
156
  activePlayback,
158
157
  Events.PLAYBACK_LEVELS_AVAILABLE,
159
- this.fillLevels,
158
+ this.onLevelsAvailable,
160
159
  )
161
160
  this.listenTo(
162
161
  activePlayback,
@@ -168,31 +167,32 @@ export class LevelSelector extends UICorePlugin {
168
167
  Events.PLAYBACK_LEVEL_SWITCH_END,
169
168
  this.onLevelSwitchEnd,
170
169
  )
171
- this.listenTo(
172
- activePlayback,
173
- Events.PLAYBACK_BITRATE,
174
- this.updateCurrentLevel,
175
- )
170
+ this.listenTo(activePlayback, Events.PLAYBACK_BITRATE, this.onBitrate)
176
171
  this.listenTo(activePlayback, Events.PLAYBACK_STOP, this.onStop)
177
172
  this.listenTo(
178
173
  activePlayback,
179
174
  Events.PLAYBACK_HIGHDEFINITIONUPDATE,
180
175
  (isHd: boolean) => {
181
176
  this.isHd = isHd
182
- this.deferRender()
177
+ this.updateHd()
183
178
  },
184
179
  )
185
180
  if (activePlayback.levels?.length > 0) {
186
- this.fillLevels(activePlayback.levels)
181
+ this.onLevelsAvailable(activePlayback.levels)
182
+ }
183
+ }
184
+
185
+ private updateHd() {
186
+ if (this.isHd) {
187
+ this.$el.find('.gear-option_hd-icon').removeClass('hidden')
188
+ } else {
189
+ this.$el.find('.gear-option_hd-icon').addClass('hidden')
187
190
  }
188
191
  }
189
192
 
190
193
  private onStop() {
191
194
  trace(`${T} onStop`)
192
195
  this.listenToOnce(this.core.activePlayback, Events.PLAYBACK_PLAY, () => {
193
- trace(`${T} on PLAYBACK_PLAY after stop`, {
194
- selectedLevelId: this.selectedLevelId,
195
- })
196
196
  if (this.core.activePlayback.getPlaybackType() === 'live') {
197
197
  if (this.selectedLevelId !== -1) {
198
198
  this.core.activePlayback.currentLevel = this.selectedLevelId
@@ -222,56 +222,53 @@ export class LevelSelector extends UICorePlugin {
222
222
  if (!this.shouldRender()) {
223
223
  return this
224
224
  }
225
-
226
- this.renderButton()
225
+ this.renderDropdown()
226
+ this.updateButton()
227
227
 
228
228
  return this
229
229
  }
230
230
 
231
- private renderButton() {
232
- if (!this.isOpen) {
233
- const html = LevelSelector.buttonTemplate({
234
- arrowRightIcon,
235
- currentText: this.currentText,
236
- isHd: this.isHd,
237
- hdIcon,
238
- })
239
- this.$el.html(html)
240
- const gear = this.core.getPlugin('bottom_gear') as BottomGear
241
- if (!gear) {
242
- trace(`${T} renderButton: bottom_gear plugin not found`)
243
- }
244
- gear?.getElement('quality')?.html(this.el)
245
- }
231
+ private renderDropdown() {
232
+ this.$el.html(
233
+ LevelSelector.listTemplate({
234
+ arrowLeftIcon,
235
+ checkIcon,
236
+ current: this.selectedLevelId,
237
+ labels: this.levelLabels,
238
+ levels: this.levels,
239
+ maxLevel: this.maxLevel,
240
+ removeAuto: this.removeAuto,
241
+ i18n: this.core.i18n,
242
+ }),
243
+ )
246
244
  }
247
245
 
248
- private renderDropdown() {
249
- const html = LevelSelector.listTemplate({
250
- arrowLeftIcon,
251
- checkIcon,
252
- labels: this.levelLabels,
253
- levels: this.levels,
254
- maxLevel: this.maxLevel,
255
- removeAuto: this.removeAuto,
256
- })
257
- this.$el.html(html)
258
- const gear = this.core.getPlugin('bottom_gear') as BottomGear
259
- trace(`${T} renderDropdown: bottom_gear plugin not found`)
260
- gear?.setContent(this.el)
246
+ private updateButton() {
247
+ ;(this.core.getPlugin('bottom_gear') as BottomGear)
248
+ ?.addItem('quality', this.$el)
249
+ .html(
250
+ LevelSelector.buttonTemplate({
251
+ arrowRightIcon,
252
+ currentText: this.currentText,
253
+ isHd: this.isHd,
254
+ hdIcon,
255
+ i18n: this.core.i18n,
256
+ }),
257
+ )
261
258
  }
262
259
 
263
260
  private get maxLevel() {
264
261
  const maxRes = this.core.options.levelSelector?.restrictResolution
265
262
  return maxRes
266
- ? this.levels.findIndex(
263
+ ? this.levels.find(
267
264
  (level) =>
268
265
  (level.height > level.width ? level.width : level.height) ===
269
266
  maxRes,
270
- )
267
+ )?.level ?? -1
271
268
  : -1
272
269
  }
273
270
 
274
- private fillLevels(levels: QualityLevel[]) {
271
+ private onLevelsAvailable(levels: QualityLevel[]) {
275
272
  const maxResolution = this.core.options.levelSelector?.restrictResolution
276
273
  this.levels = levels
277
274
  this.makeLevelsLabels()
@@ -286,77 +283,54 @@ export class LevelSelector extends UICorePlugin {
286
283
  .pop()
287
284
  this.setLevel(initialLevel?.level ?? 0)
288
285
  }
289
- this.deferRender()
286
+ this.render()
290
287
  }
291
288
 
292
289
  private makeLevelsLabels() {
293
290
  const labels = this.core.options.levelSelector?.labels ?? {}
294
291
  this.levelLabels = []
295
292
 
296
- for (let i = 0; i < this.levels.length; i++) {
297
- const level = this.levels[i]
293
+ for (const level of this.levels) {
298
294
  const ll = level.width > level.height ? level.height : level.width
299
295
  const label = labels[ll] || `${ll}p`
300
296
  this.levelLabels.push(label)
301
297
  }
302
298
  }
303
299
 
304
- private onLevelSelect(event: MouseEvent) {
300
+ private onSelect(event: MouseEvent) {
305
301
  const selectedLevel = parseInt(
306
302
  (event.currentTarget as HTMLElement)?.dataset?.id ?? '-1',
307
303
  10,
308
304
  )
309
305
  this.setLevel(selectedLevel)
306
+
310
307
  event.stopPropagation()
308
+ event.preventDefault()
311
309
  return false
312
310
  }
313
311
 
314
312
  private goBack() {
315
313
  trace(`${T} goBack`)
316
- this.isOpen = false
317
- setTimeout(() => {
318
- this.core.getPlugin('bottom_gear').refresh()
319
- }, 0);
314
+ this.core.getPlugin('bottom_gear').refresh()
320
315
  }
321
316
 
322
317
  private setLevel(index: number) {
323
- trace(`${T} setIndexLevel`, { index })
324
318
  this.selectedLevelId = index
325
- if (!this.core.activePlayback) {
326
- return
327
- }
328
- if (this.core.activePlayback.currentLevel === this.selectedLevelId) {
329
- return
330
- }
331
319
  this.core.activePlayback.currentLevel = this.selectedLevelId
332
-
333
- try {
334
- this.highlightCurrentLevel()
335
- } catch (error) {
336
- reportError(error)
337
- }
338
- this.deferRender()
339
- }
340
-
341
- private onShowLevelSelectMenu() {
342
- trace(`${T} onShowLevelSelectMenu`)
343
- this.isOpen = true
344
- this.renderDropdown()
345
320
  this.highlightCurrentLevel()
346
321
  }
347
322
 
348
323
  private allLevelElements() {
349
- return this.$('ul.gear-sub-menu li') as ZeptoResult
324
+ return this.$('#level-selector-menu li') as ZeptoResult
350
325
  }
351
326
 
352
327
  private levelElement(id = -1) {
353
328
  return (
354
- this.$(`ul.gear-sub-menu a[data-id="${id}"]`) as ZeptoResult
329
+ this.$(`#level-selector-menu a[data-id="${id}"]`) as ZeptoResult
355
330
  ).parent()
356
331
  }
357
332
 
358
333
  private onLevelSwitchStart() {
359
- this.core.activePlayback.trigger('playback:level:select:start')
360
334
  this.levelElement(this.selectedLevelId).addClass('changing')
361
335
  }
362
336
 
@@ -365,25 +339,22 @@ export class LevelSelector extends UICorePlugin {
365
339
  }
366
340
 
367
341
  private updateText(level: number) {
368
- if (level === undefined || isNaN(level)) {
369
- return
370
- }
371
342
  this.currentText = this.getLevelLabel(level)
372
- this.deferRender()
343
+ this.updateButton()
373
344
  }
374
345
 
375
346
  private getLevelLabel(id: number): string {
376
- if (id === -1) {
377
- return 'Auto'
347
+ if (id < 0) {
348
+ return this.core.i18n.t('auto')
378
349
  }
379
350
  const index = this.levels.findIndex((l) => l.level === id)
380
351
  if (index < 0) {
381
- return 'Auto'
352
+ return this.core.i18n.t('auto')
382
353
  }
383
354
  return this.levelLabels[index] ?? formatLevelLabel(this.levels[index])
384
355
  }
385
356
 
386
- private updateCurrentLevel(info: QualityLevel) {
357
+ private onBitrate(info: QualityLevel) {
387
358
  trace(`${T} updateCurrentLevel`, { info })
388
359
  this.highlightCurrentLevel()
389
360
  }
@@ -392,34 +363,23 @@ export class LevelSelector extends UICorePlugin {
392
363
  trace(`${T} highlightCurrentLevel`, {
393
364
  selectedLevelId: this.selectedLevelId,
394
365
  })
395
- this.allLevelElements().removeClass('current')
396
- this.allLevelElements().find('a').removeClass('gcore-skin-active')
366
+ this.allLevelElements()
367
+ .removeClass('current')
368
+ .find('a')
369
+ .removeClass('gcore-skin-active')
397
370
 
398
371
  const currentLevelElement = this.levelElement(this.selectedLevelId)
399
372
 
400
- currentLevelElement.addClass('current')
401
- currentLevelElement.find('a').addClass('gcore-skin-active')
373
+ currentLevelElement
374
+ .addClass('current')
375
+ .find('a')
376
+ .addClass('gcore-skin-active')
402
377
 
403
378
  this.updateText(this.selectedLevelId)
404
379
  }
405
-
406
- private deferRender = debounce(() => this.render(), 0)
407
380
  }
408
381
 
409
382
  function formatLevelLabel(level: QualityLevel): string {
410
383
  const h = level.width > level.height ? level.height : level.width
411
384
  return `${h}p`
412
385
  }
413
-
414
- function debounce(fn: () => void, wait: number) {
415
- let timerId: ReturnType<typeof setTimeout> | null = null
416
- return function () {
417
- if (timerId !== null) {
418
- clearTimeout(timerId)
419
- }
420
- timerId = setTimeout(() => {
421
- timerId = null
422
- fn()
423
- }, wait)
424
- }
425
- }
@@ -1,17 +1,16 @@
1
1
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
- import { $, UICorePlugin } from '@clappr/core'
3
- import FakeTimers from '@sinonjs/fake-timers'
4
- import { Logger, LogTracer, setTracer } from '@gcorevideo/utils'
2
+ import { Events } from '@clappr/core'
5
3
  import { LevelSelector } from '../LevelSelector.js'
6
4
  import {
5
+ createMockBottomGear,
7
6
  createMockCore,
8
7
  createMockMediaControl,
9
- createMockPlayback,
10
8
  } from '../../../testUtils.js'
11
- import { MediaControlEvents } from '../../media-control/MediaControl.js'
9
+ import { GearEvents } from '../../bottom-gear/BottomGear.js'
10
+ // import { Logger, LogTracer, setTracer } from '@gcorevideo/utils'
12
11
 
13
- setTracer(new LogTracer('LevelSelector.test'))
14
- Logger.enable('*')
12
+ // setTracer(new LogTracer('LevelSelector.test'))
13
+ // Logger.enable('*')
15
14
 
16
15
  const LEVELS = [
17
16
  {
@@ -33,28 +32,20 @@ const LEVELS = [
33
32
  bitrate: 250000,
34
33
  },
35
34
  ]
35
+
36
36
  describe('LevelSelector', () => {
37
- let clock: FakeTimers.InstalledClock
38
37
  let core: any
39
38
  let levelSelector: LevelSelector
40
- let activePlayback: any
41
- let mediaControl: UICorePlugin
42
- let bottomGear: UICorePlugin | null
43
- beforeEach(() => {
44
- clock = FakeTimers.install()
45
- })
46
- afterEach(() => {
47
- clock.uninstall()
48
- })
39
+ let mediaControl: any
40
+ let bottomGear: any
49
41
  describe('basically', () => {
50
42
  beforeEach(() => {
51
43
  core = createMockCore({
52
44
  levelSelector: {
53
45
  // restrictResolution: 360,
54
- labels: { 360: '360p', 720: 'HD' },
46
+ labels: { 720: 'HD', 1080: 'Full HD' },
55
47
  },
56
48
  })
57
- activePlayback = core.activePlayback
58
49
  core.getPlugin.mockImplementation((name: string) => {
59
50
  if (name === 'media_control') {
60
51
  return mediaControl
@@ -65,43 +56,45 @@ describe('LevelSelector', () => {
65
56
  return null
66
57
  })
67
58
  mediaControl = createMockMediaControl(core)
68
- bottomGear = createBottomGear(core)
59
+ bottomGear = createMockBottomGear(core)
69
60
  levelSelector = new LevelSelector(core)
70
61
  })
71
62
  describe('initially', () => {
72
- beforeEach(async () => {
73
- core.emit('core:active:container:changed')
74
- await clock.tickAsync(1)
75
- activePlayback.emit('playback:levels:available', LEVELS)
76
- await clock.tickAsync(1)
63
+ beforeEach(() => {
64
+ core.emit(Events.CORE_READY)
65
+ core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED)
66
+ bottomGear.trigger(GearEvents.RENDERED)
67
+ core.activePlayback.emit(Events.PLAYBACK_LEVELS_AVAILABLE, LEVELS)
77
68
  })
78
69
  it('should render proper level label', () => {
79
- // @ts-ignore
80
- expect(levelSelector.el.textContent).toMatchQualityLevelLabel('Auto')
70
+ expect(
71
+ bottomGear.$el.find('[data-quality]').text(),
72
+ // @ts-ignore
73
+ ).toMatchQualityLevelLabel('auto')
81
74
  })
82
75
  })
83
76
  describe.each([
84
- ['auto', LEVELS, -1, 'Auto'],
77
+ ['auto', LEVELS, -1, 'auto'],
85
78
  ['standard label', LEVELS, 0, '360p'],
86
79
  ['custom label', LEVELS, 1, 'HD'],
87
80
  ])('%s', (_, levels, current, label) => {
88
- beforeEach(async () => {
89
- core.emit('core:active:container:changed')
90
- await clock.tickAsync(1)
91
- // activePlayback.currentLevel = current
92
- activePlayback.emit('playback:levels:available', levels)
93
- await clock.tickAsync(1)
94
- levelSelector.$el.find('.gear-option').click()
95
- await clock.tickAsync(1)
81
+ beforeEach(() => {
82
+ core.emit(Events.CORE_READY)
83
+ core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED)
84
+ bottomGear.trigger(GearEvents.RENDERED)
85
+ core.activePlayback.emit(Events.PLAYBACK_LEVELS_AVAILABLE, levels)
96
86
  levelSelector.$el
97
- .find(`.gear-sub-menu_btn[data-id="${current}"]`)
87
+ .find(`#level-selector-menu [data-id="${current}"]`)
98
88
  .click()
99
- await clock.tickAsync(1)
100
89
  })
101
- it('should render the proper level labels', () => {
102
- expect(levelSelector.el.innerHTML).toMatchSnapshot()
90
+ it('should render the proper level label', () => {
91
+ expect(
92
+ bottomGear.$el.find('[data-quality]').text(),
93
+ // @ts-ignore
94
+ ).toMatchQualityLevelLabel(label)
103
95
  })
104
96
  it('should render the selected level', () => {
97
+ expect(levelSelector.el.innerHTML).toMatchSnapshot()
105
98
  expect(
106
99
  levelSelector.$el.find('ul.gear-sub-menu .current')[0].textContent,
107
100
  // @ts-ignore
@@ -111,15 +104,14 @@ describe('LevelSelector', () => {
111
104
  })
112
105
  describe('options.restrictResolution', () => {
113
106
  beforeEach(() => {
114
- let mediaControl: UICorePlugin | null = null
115
- let bottomGear: UICorePlugin | null = null
116
107
  core = createMockCore({
117
108
  levelSelector: {
118
109
  restrictResolution: 360,
119
- labels: { 360: '360p', 720: '720p' },
110
+ labels: { 360: '360p', 720: '720p', 1080: '1080p' },
120
111
  },
121
112
  })
122
- activePlayback = core.activePlayback
113
+ mediaControl = createMockMediaControl(core)
114
+ bottomGear = createMockBottomGear(core)
123
115
  core.getPlugin.mockImplementation((name: string) => {
124
116
  if (name === 'media_control') {
125
117
  return mediaControl
@@ -130,30 +122,31 @@ describe('LevelSelector', () => {
130
122
  return null
131
123
  })
132
124
  mediaControl = createMockMediaControl(core)
133
- bottomGear = createBottomGear(core)
125
+ bottomGear = createMockBottomGear(core)
134
126
  levelSelector = new LevelSelector(core)
127
+ core.emit(Events.CORE_READY)
128
+ core.emit(Events.CORE_ACTIVE_CONTAINER_CHANGED, core.activeContainer)
129
+ bottomGear.trigger(GearEvents.RENDERED)
135
130
  })
136
- describe('basically', () => {
131
+ describe('initially', () => {
137
132
  beforeEach(async () => {
138
- core.emit('core:active:container:changed')
139
- await clock.tickAsync(1)
140
- activePlayback.emit('playback:levels:available', LEVELS)
141
- await clock.tickAsync(1)
133
+ core.activePlayback.emit(Events.PLAYBACK_LEVELS_AVAILABLE, LEVELS)
142
134
  })
143
135
  it('should render the restricted quality level label', () => {
136
+ expect(bottomGear.$el.find('[data-quality]').html()).toMatchSnapshot()
144
137
  expect(
145
- levelSelector.el.textContent,
138
+ bottomGear.$el.find('[data-quality]').text(),
146
139
  // @ts-ignore
147
140
  ).toMatchQualityLevelLabel('360p')
148
-
149
- expect(levelSelector.el.innerHTML).toMatchSnapshot()
141
+ expect(
142
+ levelSelector.$el.find('#level-selector-menu .current').text(),
143
+ // @ts-ignore
144
+ ).toMatchQualityLevelOption('360p')
150
145
  })
151
146
  })
152
147
  describe('given vertical video format levels', () => {
153
- beforeEach(async () => {
154
- core.emit('core:active:container:changed')
155
- await clock.tickAsync(1)
156
- activePlayback.emit('playback:levels:available', [
148
+ beforeEach(() => {
149
+ core.activePlayback.emit(Events.PLAYBACK_LEVELS_AVAILABLE, [
157
150
  {
158
151
  level: 0,
159
152
  width: 360,
@@ -168,22 +161,31 @@ describe('LevelSelector', () => {
168
161
  },
169
162
  {
170
163
  level: 2,
171
- width: 1920,
172
- height: 1080,
164
+ width: 1080,
165
+ height: 1920,
173
166
  bitrate: 250000,
174
167
  },
175
168
  ])
176
- await clock.tickAsync(1)
177
- levelSelector.$el.find('.gear-option').click()
178
- await clock.tickAsync(1)
179
169
  })
180
170
  it('should recognize vertical orientation', () => {
181
171
  expect(levelSelector.el.innerHTML).toMatchSnapshot()
182
- expect(levelSelector.el.innerHTML).toMatchSnapshot()
172
+ expect(
173
+ levelSelector.$el.find('#level-selector-menu [data-id]:eq(0)').text(),
174
+ // @ts-ignore
175
+ ).toMatchQualityLevelOption('1080p')
176
+ expect(
177
+ levelSelector.$el.find('#level-selector-menu [data-id]:eq(1)').text(),
178
+ // @ts-ignore
179
+ ).toMatchQualityLevelOption('720p')
180
+ expect(
181
+ levelSelector.$el.find('#level-selector-menu [data-id]:eq(2)').text(),
182
+ // @ts-ignore
183
+ ).toMatchQualityLevelOption('360p')
183
184
  })
184
185
  it('should properly apply the restriction', () => {
185
186
  expect(
186
- levelSelector.$el.find('li:not(.level-disabled)')[0].textContent,
187
+ levelSelector.$el.find('#level-selector-menu li:not(.disabled)')[0]
188
+ .textContent,
187
189
  // @ts-ignore
188
190
  ).toMatchQualityLevelOption('360p')
189
191
  })
@@ -195,11 +197,11 @@ expect.extend({
195
197
  toMatchQualityLevelLabel(received, expected) {
196
198
  const { isNot } = this
197
199
  const rendered = received
198
- .replace('/assets/icons/new/arrow-right.svg', '')
200
+ .replace(/\/assets\/.*\.svg/g, '')
199
201
  .replace(/\s+/g, ' ')
200
202
  .trim()
201
203
  return {
202
- pass: rendered.includes(`Quality ${expected}`),
204
+ pass: rendered.includes(`quality ${expected}`),
203
205
  message: () =>
204
206
  `Quality label must${
205
207
  isNot ? ' not' : ''
@@ -209,7 +211,7 @@ expect.extend({
209
211
  toMatchQualityLevelOption(received, expected) {
210
212
  const { isNot } = this
211
213
  const rendered = received
212
- .replace('/assets/icons/new/check.svg', '')
214
+ .replace(/\/assets\/.*\.svg/g, '')
213
215
  .replace(/\s+/g, ' ')
214
216
  .trim()
215
217
  return {
@@ -221,15 +223,3 @@ expect.extend({
221
223
  }
222
224
  },
223
225
  })
224
-
225
- function createBottomGear(core: any) {
226
- const bottomGear = new UICorePlugin(core)
227
- const elemets = {
228
- quality: $(document.createElement('div')),
229
- }
230
- // @ts-ignore
231
- bottomGear.getElement = vi.fn().mockImplementation((name) => elemets[name])
232
- // @ts-ignore
233
- bottomGear.setContent = vi.fn()
234
- return bottomGear
235
- }