Files
edx-platform/xmodule/js/spec/video/video_context_menu_spec.js
Syed Ali Abbas Zaidi 8480dbc228 chore: apply amnesty on existing not fixable issues (#32215)
* fix: eslint operator-linebreak issue

* fix: eslint quotes issue

* fix: react jsx indent and props issues

* fix: eslint trailing spaces issues

* fix: eslint line around directives issue

* fix: eslint semi rule

* fix: eslint newline per chain rule

* fix: eslint space infix ops rule

* fix: eslint space-in-parens issue

* fix: eslint space before function paren issue

* fix: eslint space before blocks issue

* fix: eslint arrow body style issue

* fix: eslint dot-location issue

* fix: eslint quotes issue

* fix: eslint quote props issue

* fix: eslint operator assignment issue

* fix: eslint new line after import issue

* fix: indent issues

* fix: operator assignment issue

* fix: all autofixable eslint issues

* fix: all react related fixable issues

* fix: autofixable eslint issues

* chore: remove all template literals

* fix: remaining autofixable issues

* chore: apply amnesty on all existing issues

* fix: failing xss-lint issues

* refactor: apply amnesty on remaining issues

* refactor: apply amnesty on new issues

* fix: remove file level suppressions

* refactor: apply amnesty on new issues
2023-08-07 19:13:19 +05:00

440 lines
20 KiB
JavaScript

/* globals _ */
(function() {
'use strict';
describe('Video Context Menu', function() {
var state, openMenu, keyPressEvent, openSubmenuMouse, openSubmenuKeyboard, closeSubmenuMouse,
closeSubmenuKeyboard, menu, menuItems, menuSubmenuItem, submenu, submenuItems, overlay, $playButton;
openMenu = function() {
var $container = $('.video');
$container.find('video').trigger('contextmenu');
menu = $container.children('.contextmenu');
menuItems = menu.children('.menu-item').not('.submenu-item');
menuSubmenuItem = menu.children('.menu-item.submenu-item');
submenu = menuSubmenuItem.children('.submenu');
submenuItems = submenu.children('.menu-item');
overlay = $container.children('.overlay');
$playButton = $('.video_control.play');
};
keyPressEvent = function(key) {
return $.Event('keydown', {keyCode: key});
};
// eslint-disable-next-line no-shadow
openSubmenuMouse = function(menuSubmenuItem) {
menuSubmenuItem.mouseover();
jasmine.clock().tick(200);
expect(menuSubmenuItem).toHaveClass('is-opened');
};
// eslint-disable-next-line no-shadow
openSubmenuKeyboard = function(menuSubmenuItem, keyCode) {
menuSubmenuItem.focus().trigger(keyPressEvent(keyCode || $.ui.keyCode.RIGHT));
expect(menuSubmenuItem).toHaveClass('is-opened');
expect(menuSubmenuItem.children().last().children().first()).toBeFocused();
};
// eslint-disable-next-line no-shadow
closeSubmenuMouse = function(menuSubmenuItem) {
menuSubmenuItem.mouseleave();
jasmine.clock().tick(200);
expect(menuSubmenuItem).not.toHaveClass('is-opened');
};
// eslint-disable-next-line no-shadow
closeSubmenuKeyboard = function(menuSubmenuItem) {
menuSubmenuItem.children().first().focus().trigger(keyPressEvent($.ui.keyCode.LEFT));
expect(menuSubmenuItem).not.toHaveClass('is-opened');
expect(menuSubmenuItem).toBeFocused();
};
beforeEach(function() {
jasmine.clock().install();
// $.cookie is mocked, make sure we have a state with an unmuted volume.
$.cookie.and.returnValue('100');
jasmine.addMatchers({
toHaveCorrectLabels: function() {
return {
compare: function(actual, labelsList) {
return {
pass: _.difference(labelsList, _.map(actual, function(item) {
return $(item).text();
})).length === 0
};
}
};
}
});
});
afterEach(function() {
$('source').remove();
_.result(state.storage, 'clear');
_.result($('video').data('contextmenu'), 'destroy');
_.result(state.videoPlayer, 'destroy');
jasmine.clock().uninstall();
});
describe('constructor', function() {
it('the structure should be created on first `contextmenu` call', function() {
state = jasmine.initializePlayer();
expect(menu).not.toExist();
openMenu();
/*
Make sure we have the expected HTML structure:
- Play (Pause)
- Mute (Unmute)
- Fill browser (Exit full browser)
- Speed >
- 0.75x
- 1.0x
- 1.25x
- 1.50x
*/
// Only one context menu per video container
expect(menu).toBeInDOM();
expect(menu).toHaveClass('is-opened');
expect(menuItems).toHaveCorrectLabels(['Play', 'Mute', 'Fill browser']);
expect(menuSubmenuItem.children('span')).toHaveText('Speed');
expect(submenuItems).toHaveCorrectLabels(['0.75x', '1.0x', '1.25x', '1.50x']);
// Check that one of the speed submenu item is selected
expect(_.size(submenuItems.filter('.is-selected'))).toBe(1);
});
it('add ARIA attributes to menu, menu items, submenu and submenu items', function() {
state = jasmine.initializePlayer();
openMenu();
// Menu and its items.
expect(menu).toHaveAttr('role', 'menu');
menuItems.each(function() {
expect($(this)).toHaveAttrs({
'aria-selected': 'false',
role: 'menuitem'
});
});
expect(menuSubmenuItem).toHaveAttrs({
'aria-expanded': 'false',
'aria-haspopup': 'true',
role: 'menuitem'
});
// Submenu and its items.
expect(submenu).toHaveAttr('role', 'menu');
submenuItems.each(function() {
expect($(this)).toHaveAttr('role', 'menuitem');
expect($(this)).toHaveAttr('aria-selected');
});
});
it('is not used by Youtube type of video player', function() {
state = jasmine.initializePlayer('video.html');
expect($('video, iframe')).not.toHaveData('contextmenu');
});
});
describe('methods:', function() {
beforeEach(function() {
state = jasmine.initializePlayer();
openMenu();
});
it('menu can be destroyed successfully', function() {
var menuitemEvents = ['click', 'keydown', 'contextmenu', 'mouseover'],
menuEvents = ['keydown', 'contextmenu', 'mouseleave', 'mouseover'];
menu.data('menu').destroy();
expect(menu).not.toBeInDOM();
expect(overlay).not.toBeInDOM();
_.each(menuitemEvents, function(eventName) {
expect(menuItems.first()).not.toHandle(eventName);
});
_.each(menuEvents, function(eventName) {
expect(menuSubmenuItem).not.toHandle(eventName);
});
_.each(menuEvents, function(eventName) {
expect(menu).not.toHandle(eventName);
});
expect($('video')).not.toHandle('contextmenu');
expect($('video')).not.toHaveData('contextmenu');
});
it('can change label for the submenu', function() {
expect(menuSubmenuItem.children('span')).toHaveText('Speed');
menuSubmenuItem.data('menu').setLabel('New Name');
expect(menuSubmenuItem.children('span')).toHaveText('New Name');
});
it('can change label for the menuitem', function() {
expect(menuItems.first()).toHaveText('Play');
menuItems.first().data('menu').setLabel('Pause');
expect(menuItems.first()).toHaveText('Pause');
});
});
describe('when video is right-clicked', function() {
beforeEach(function() {
state = jasmine.initializePlayer();
jasmine.mockFullscreenAPI();
openMenu();
});
it('context menu opens', function() {
expect(menu).toHaveClass('is-opened');
expect(overlay).toBeInDOM();
});
it('mouseover and mouseleave behave as expected', function() {
openSubmenuMouse(menuSubmenuItem);
expect(menuSubmenuItem).toHaveClass('is-opened');
closeSubmenuMouse(menuSubmenuItem);
expect(menuSubmenuItem).not.toHaveClass('is-opened');
submenuItems.eq(1).mouseover();
expect(submenuItems.eq(1)).toBeFocused();
});
it('mouse left-clicking outside of the context menu will close it', function() {
// Left-click outside of open menu, for example on Play button
$playButton.click();
expect(menu).not.toHaveClass('is-opened');
expect(overlay).not.toBeInDOM();
});
it('mouse right-clicking outside of video will close it', function() {
// Right-click outside of open menu for example on Play button
$playButton.trigger('contextmenu');
expect(menu).not.toHaveClass('is-opened');
expect(overlay).not.toBeInDOM();
});
it('mouse right-clicking inside video but outside of context menu will not close it', function() {
spyOn(menu.data('menu'), 'pointInContainerBox').and.returnValue(true);
overlay.trigger('contextmenu');
expect(menu).toHaveClass('is-opened');
expect(overlay).toBeInDOM();
});
it('mouse right-clicking inside video but outside of context menu will close submenus', function() {
spyOn(menu.data('menu'), 'pointInContainerBox').and.returnValue(true);
openSubmenuMouse(menuSubmenuItem);
expect(menuSubmenuItem).toHaveClass('is-opened');
overlay.trigger('contextmenu');
expect(menuSubmenuItem).not.toHaveClass('is-opened');
});
it('mouse left/right-clicking behaves as expected on play/pause menu item', function() {
var menuItem = menuItems.first();
spyOn(state.videoPlayer, 'isPlaying');
spyOn(state.videoPlayer, 'play').and.callFake(function() {
state.videoPlayer.isPlaying.and.returnValue(true);
state.el.trigger('play');
});
spyOn(state.videoPlayer, 'pause').and.callFake(function() {
state.videoPlayer.isPlaying.and.returnValue(false);
state.el.trigger('pause');
});
// Left-click on play
menuItem.click();
expect(state.videoPlayer.play).toHaveBeenCalled();
expect(menuItem).toHaveText('Pause');
openMenu();
// Left-click on pause
menuItem.click();
expect(state.videoPlayer.pause).toHaveBeenCalled();
expect(menuItem).toHaveText('Play');
state.videoPlayer.play.calls.reset();
// Right-click on play
menuItem.trigger('contextmenu');
expect(state.videoPlayer.play).toHaveBeenCalled();
expect(menuItem).toHaveText('Pause');
});
it('mouse left/right-clicking behaves as expected on mute/unmute menu item', function() {
var menuItem = menuItems.eq(1);
// Left-click on mute
menuItem.click();
expect(state.videoVolumeControl.getMuteStatus()).toBe(true);
expect(menuItem).toHaveText('Unmute');
openMenu();
// Left-click on unmute
menuItem.click();
expect(state.videoVolumeControl.getMuteStatus()).toBe(false);
expect(menuItem).toHaveText('Mute');
// Right-click on mute
menuItem.trigger('contextmenu');
expect(state.videoVolumeControl.getMuteStatus()).toBe(true);
expect(menuItem).toHaveText('Unmute');
openMenu();
// Right-click on unmute
menuItem.trigger('contextmenu');
expect(state.videoVolumeControl.getMuteStatus()).toBe(false);
expect(menuItem).toHaveText('Mute');
});
it('mouse left/right-clicking behaves as expected on go to Exit full browser menu item', function() {
var menuItem = menuItems.eq(2);
// Left-click on Fill browser
menuItem.click();
expect(state.isFullScreen).toBe(true);
expect(menuItem).toHaveText('Exit full browser');
openMenu();
// Left-click on Exit full browser
menuItem.click();
expect(state.isFullScreen).toBe(false);
expect(menuItem).toHaveText('Fill browser');
// Right-click on Fill browser
menuItem.trigger('contextmenu');
expect(state.isFullScreen).toBe(true);
expect(menuItem).toHaveText('Exit full browser');
openMenu();
// Right-click on Exit full browser
menuItem.trigger('contextmenu');
expect(state.isFullScreen).toBe(false);
expect(menuItem).toHaveText('Fill browser');
});
it('mouse left/right-clicking behaves as expected on speed submenu item', function() {
// Set speed to 0.75x
state.videoSpeedControl.setSpeed('0.75');
// Left-click on second submenu speed (1.0x)
openSubmenuMouse(menuSubmenuItem);
submenuItems.eq(1).click();
// Expect speed to be 1.0x
expect(state.videoSpeedControl.currentSpeed).toBe('1.0');
// Expect speed submenu item 0.75x not to be active
expect(submenuItems.first()).not.toHaveClass('is-selected');
// Expect speed submenu item 1.0x to be active
expect(submenuItems.eq(1)).toHaveClass('is-selected');
// Set speed to 0.75x
state.videoSpeedControl.setSpeed('0.75');
// Right-click on second submenu speed (1.0x)
openSubmenuMouse(menuSubmenuItem);
submenuItems.eq(1).trigger('contextmenu');
// Expect speed to be 1.0x
expect(state.videoSpeedControl.currentSpeed).toBe('1.0');
// Expect speed submenu item 0.75x not to be active
expect(submenuItems.first()).not.toHaveClass('is-selected');
// Expect speed submenu item 1.0x to be active
expect(submenuItems.eq(1)).toHaveClass('is-selected');
});
});
describe('Keyboard interactions', function() {
beforeEach(function() {
state = jasmine.initializePlayer();
openMenu();
});
it('focus the first item of the just opened menu on UP keydown', function() {
menu.trigger(keyPressEvent($.ui.keyCode.UP));
expect(menuSubmenuItem).toBeFocused();
});
it('focus the last item of the just opened menu on DOWN keydown', function() {
menu.trigger(keyPressEvent($.ui.keyCode.DOWN));
expect(menuItems.first()).toBeFocused();
});
it('open the submenu on ENTER keydown', function() {
openSubmenuKeyboard(menuSubmenuItem, $.ui.keyCode.ENTER);
expect(menuSubmenuItem).toHaveClass('is-opened');
expect(submenuItems.first()).toBeFocused();
});
it('open the submenu on SPACE keydown', function() {
openSubmenuKeyboard(menuSubmenuItem, $.ui.keyCode.SPACE);
expect(menuSubmenuItem).toHaveClass('is-opened');
expect(submenuItems.first()).toBeFocused();
});
it('open the submenu on RIGHT keydown', function() {
openSubmenuKeyboard(menuSubmenuItem, $.ui.keyCode.RIGHT);
expect(menuSubmenuItem).toHaveClass('is-opened');
expect(submenuItems.first()).toBeFocused();
});
it('close the menu on ESCAPE keydown', function() {
menu.trigger(keyPressEvent($.ui.keyCode.ESCAPE));
expect(menu).not.toHaveClass('is-opened');
expect(overlay).not.toBeInDOM();
});
it('close the submenu on ESCAPE keydown', function() {
openSubmenuKeyboard(menuSubmenuItem);
menuSubmenuItem.trigger(keyPressEvent($.ui.keyCode.ESCAPE));
expect(menuSubmenuItem).not.toHaveClass('is-opened');
expect(overlay).not.toBeInDOM();
});
it('close the submenu on LEFT keydown on submenu items', function() {
closeSubmenuKeyboard(menuSubmenuItem);
});
it('do nothing on RIGHT keydown on submenu item', function() {
submenuItems.eq(1).focus().trigger(keyPressEvent($.ui.keyCode.RIGHT)); // Mute
// Is still focused.
expect(submenuItems.eq(1)).toBeFocused();
});
it('do nothing on TAB keydown on menu item', function() {
submenuItems.eq(1).focus().trigger(keyPressEvent($.ui.keyCode.TAB)); // Mute
// Is still focused.
expect(submenuItems.eq(1)).toBeFocused();
});
it('UP and DOWN keydown function as expected on menu/submenu items', function() {
menuItems.eq(0).focus(); // Play
expect(menuItems.eq(0)).toBeFocused();
menuItems.eq(0).trigger(keyPressEvent($.ui.keyCode.DOWN));
expect(menuItems.eq(1)).toBeFocused(); // Mute
menuItems.eq(1).trigger(keyPressEvent($.ui.keyCode.DOWN));
expect(menuItems.eq(2)).toBeFocused(); // Fullscreen
menuItems.eq(2).trigger(keyPressEvent($.ui.keyCode.DOWN));
expect(menuSubmenuItem).toBeFocused(); // Speed
menuSubmenuItem.trigger(keyPressEvent($.ui.keyCode.DOWN));
expect(menuItems.eq(0)).toBeFocused(); // Play
menuItems.eq(0).trigger(keyPressEvent($.ui.keyCode.UP));
expect(menuSubmenuItem).toBeFocused(); // Speed
// Check if hidden item can be skipped correctly.
menuItems.eq(2).hide(); // hide Fullscreen item
menuSubmenuItem.trigger(keyPressEvent($.ui.keyCode.UP));
expect(menuItems.eq(1)).toBeFocused(); // Mute
menuItems.eq(1).trigger(keyPressEvent($.ui.keyCode.UP));
expect(menuItems.eq(0)).toBeFocused(); // Play
});
it('current item is still focused if all siblings are hidden', function() {
menuItems.eq(0).focus(); // Play
expect(menuItems.eq(0)).toBeFocused(); // hide all siblings
menuItems.eq(0).siblings().hide();
menuSubmenuItem.trigger(keyPressEvent($.ui.keyCode.DOWN));
expect(menuItems.eq(0)).toBeFocused();
menuSubmenuItem.trigger(keyPressEvent($.ui.keyCode.UP));
expect(menuItems.eq(0)).toBeFocused();
});
it('ENTER keydown on menu/submenu item selects its data and closes menu', function() {
menuItems.eq(2).focus().trigger(keyPressEvent($.ui.keyCode.ENTER)); // Fullscreen
expect(menuItems.eq(2)).toHaveClass('is-selected');
expect(menuItems.eq(2).siblings()).not.toHaveClass('is-selected');
expect(state.isFullScreen).toBeTruthy();
expect(menuItems.eq(2)).toHaveText('Exit full browser');
});
it('SPACE keydown on menu/submenu item selects its data and closes menu', function() {
submenuItems.eq(2).focus().trigger(keyPressEvent($.ui.keyCode.SPACE)); // 1.25x
expect(submenuItems.eq(2)).toHaveClass('is-selected');
expect(submenuItems.eq(2).siblings()).not.toHaveClass('is-selected');
expect(state.videoSpeedControl.currentSpeed).toBe('1.25');
});
});
});
}());