diff --git a/js&css/web-accessible/www.youtube.com/shortcuts.js b/js&css/web-accessible/www.youtube.com/shortcuts.js index cc7035f17..7ca4d6a12 100644 --- a/js&css/web-accessible/www.youtube.com/shortcuts.js +++ b/js&css/web-accessible/www.youtube.com/shortcuts.js @@ -483,16 +483,52 @@ ImprovedTube.shortcutActivateCaptions = function () { }; /*------Chapters------*/ ImprovedTube.shortcutChapters = function () { - const available = document.querySelector('[target-id*=chapters][visibility*=HIDDEN]') || document.querySelector('[target-id*=chapters]').clientHeight; + const fullscreenElement = + document.fullscreenElement || + document.webkitFullscreenElement || + document.querySelector('.html5-video-player.ytp-fullscreen'); + + if (fullscreenElement) { + const fullscreenChapters = fullscreenElement.querySelector( + '.ytp-chapter-title' + ); + + if (fullscreenChapters) { + fullscreenChapters.click(); + return; + } + } + + const chaptersPanel = document.querySelector('[target-id*=chapters]'); + + if (!chaptersPanel) { + return; + } + + const modernChapters = document.querySelector( + '[modern-chapters] #navigation-button button[aria-label]' + ); + + const visibilityButton = document.querySelector( + '[target-id*=chapters][visibility*=EXPANDED] #visibility-button button[aria-label]' + ); + + const available = + document.querySelector('[target-id*=chapters][visibility*=HIDDEN]') || + chaptersPanel.clientHeight; + if (available) { - const modernChapters = document.querySelector('[modern-chapters] #navigation-button button[aria-label]'); - modernChapters ? modernChapters.click() : document.querySelector('[target-id*=chapters]')?.removeAttribute('visibility'); + if (modernChapters) { + modernChapters.click(); + } else { + chaptersPanel.removeAttribute('visibility'); + } + } else if (visibilityButton) { + visibilityButton.click(); } else { - const visibilityButton = document.querySelector('[target-id*=chapters][visibility*=EXPANDED] #visibility-button button[aria-label]'); - visibilityButton ? visibilityButton.click() : document.querySelector('*[target-id*=chapters] #visibility-button button')?.click(); - } - if (!modernChapters && visibilityButton) { - console.error('shortcutChapters: Cant fint proper Enable button, falling back to unreliable bruteforce method'); + chaptersPanel + .querySelector('#visibility-button button') + ?.click(); } }; /*------Transcript------*/ diff --git a/tests/unit/shortcut-chapters-fullscreen.test.js b/tests/unit/shortcut-chapters-fullscreen.test.js new file mode 100644 index 000000000..019ebde0a --- /dev/null +++ b/tests/unit/shortcut-chapters-fullscreen.test.js @@ -0,0 +1,80 @@ +const fs = require('fs'); +const path = require('path'); +const vm = require('vm'); + +const SHORTCUTS_SRC = path.join( + __dirname, + '../../js&css/web-accessible/www.youtube.com/shortcuts.js' +); + +function loadShortcuts(documentMock) { + const improvedTube = {}; + + const sandbox = { + ImprovedTube: improvedTube, + document: documentMock, + window: {}, + console, + setTimeout, + clearTimeout + }; + + vm.createContext(sandbox); + + vm.runInContext( + fs.readFileSync(SHORTCUTS_SRC, 'utf8'), + sandbox + ); + + return improvedTube; +} + +describe('Issue #4283: chapters shortcut in fullscreen', () => { + test('clicks the fullscreen chapters control when available', () => { + const chaptersButton = { + click: jest.fn() + }; + + const fullscreenElement = { + querySelector: jest.fn((selector) => { + if (selector === '.ytp-chapter-title') { + return chaptersButton; + } + + return null; + }) + }; + + const documentMock = { + fullscreenElement, + webkitFullscreenElement: null, + + querySelector: jest.fn(() => null) + }; + + const improvedTube = loadShortcuts(documentMock); + + improvedTube.shortcutChapters(); + + expect(fullscreenElement.querySelector) + .toHaveBeenCalledWith('.ytp-chapter-title'); + + expect(chaptersButton.click) + .toHaveBeenCalledTimes(1); + }); + + test('does not throw when chapters UI is unavailable', () => { + const documentMock = { + fullscreenElement: null, + webkitFullscreenElement: null, + + querySelector: jest.fn(() => null) + }; + + const improvedTube = loadShortcuts(documentMock); + + expect(() => { + improvedTube.shortcutChapters(); + }).not.toThrow(); + }); +}); \ No newline at end of file