mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-08 19:06:15 -08:00
253 lines
12 KiB
JavaScript
253 lines
12 KiB
JavaScript
import { expect, test } from './fixtures.js'
|
|
|
|
async function setup(page, mode = 'normal') {
|
|
await page.goto('/test/player.html?core=candidate&chapter=published')
|
|
await page.evaluate((mode) => {
|
|
window.selectorCalls = []
|
|
window.selectorClicks = []
|
|
window.selectorHotkeys = []
|
|
window.pendingSelections = {}
|
|
window.art = new window.Artplayer({ container: '.player', url: '/test/pattern.mp4', muted: true, hotkey: true, fullscreenWeb: true })
|
|
window.art.on('hotkey', event => window.selectorHotkeys.push(event.code))
|
|
const selector = mode === 'empty'
|
|
? []
|
|
: mode === 'complex'
|
|
? [{ html: '<button id="nested-option">Native option</button>', value: 'native' }, { html: '<input id="nested-input" value="Editable">', value: 'input' }]
|
|
: ['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel'].map((html, index) => ({ html, value: html, default: index === 1 }))
|
|
window.art.controls.add({ name: 'keyboard-selector', position: 'right', html: 'Bravo', tooltip: 'Quality', selector, click(component, event) {
|
|
window.selectorClicks.push([this === window.art, component === this.controls, event.type])
|
|
}, onSelect(item, element, event) {
|
|
window.selectorCalls.push([this === window.art, item.value, element === item.$control_item, event.type])
|
|
if (mode === 'async')
|
|
return new Promise(resolve => window.pendingSelections[item.value] = resolve)
|
|
if (mode === 'external-focus')
|
|
document.querySelector('#play').focus()
|
|
return item.html
|
|
} })
|
|
}, mode)
|
|
await expect.poll(() => page.evaluate(() => window.art.isReady)).toBe(true)
|
|
}
|
|
|
|
const trigger = page => page.locator('.art-control-keyboard-selector .art-selector-value')
|
|
const list = page => page.locator('.art-control-keyboard-selector .art-selector-list')
|
|
const item = (page, index) => list(page).locator('.art-selector-item').nth(index)
|
|
|
|
test('candidate: selector Tab entry, focus navigation and typeahead do not commit before Enter', async ({ page }, testInfo) => {
|
|
await setup(page)
|
|
await page.locator('.art-control-playAndPause').focus()
|
|
for (let attempt = 0; attempt < 8 && !await trigger(page).evaluate(element => document.activeElement === element); attempt++)
|
|
await page.keyboard.press('Tab')
|
|
await expect(trigger(page)).toBeFocused()
|
|
await expect(trigger(page)).toHaveAttribute('aria-haspopup', 'listbox')
|
|
await expect(trigger(page)).toHaveAttribute('aria-expanded', 'false')
|
|
await expect(list(page)).toHaveAttribute('role', 'listbox')
|
|
await expect(list(page)).toHaveAttribute('aria-label', 'Quality')
|
|
await expect(list(page)).toBeHidden()
|
|
await page.keyboard.press('ArrowDown')
|
|
await expect(item(page, 1)).toBeFocused()
|
|
await expect(item(page, 1)).toHaveAttribute('aria-selected', 'true')
|
|
await page.keyboard.press('ArrowDown')
|
|
await expect(item(page, 2)).toBeFocused()
|
|
await expect(item(page, 2)).toHaveAttribute('aria-selected', 'false')
|
|
await page.keyboard.press('f')
|
|
await expect(item(page, 5)).toBeFocused()
|
|
await page.keyboard.press('Home')
|
|
await expect(item(page, 0)).toBeFocused()
|
|
await page.keyboard.press('End')
|
|
await expect(item(page, 7)).toBeFocused()
|
|
expect(await page.evaluate(() => window.selectorCalls)).toEqual([])
|
|
expect(await page.evaluate(() => window.art.currentTime)).toBe(0)
|
|
expect(await page.evaluate(() => window.art.volume)).toBe(0.7)
|
|
await page.locator('.player').screenshot({ path: testInfo.outputPath('selector-keyboard.png') })
|
|
await page.keyboard.press('Enter')
|
|
await expect(trigger(page)).toBeFocused()
|
|
await expect(trigger(page)).toHaveText('Hotel')
|
|
await expect(trigger(page)).toHaveAttribute('aria-expanded', 'false')
|
|
await expect(list(page)).toBeHidden()
|
|
await expect(item(page, 7)).toHaveAttribute('aria-selected', 'true')
|
|
await expect(item(page, 1)).toHaveAttribute('aria-selected', 'false')
|
|
expect(await page.evaluate(() => window.selectorCalls)).toEqual([[true, 'Hotel', true, 'click']])
|
|
expect(await page.evaluate(() => window.selectorHotkeys)).toEqual([])
|
|
expect(await page.locator('.art-control-keyboard-selector').getAttribute('role')).toBeNull()
|
|
})
|
|
|
|
test('candidate: selector Space commits once and Escape closes before web fullscreen exits', async ({ page }) => {
|
|
await setup(page)
|
|
await page.evaluate(() => {
|
|
window.art.fullscreenWeb = true
|
|
})
|
|
await trigger(page).focus()
|
|
await page.keyboard.down('Space')
|
|
await page.keyboard.down('Space')
|
|
await expect(list(page)).toBeHidden()
|
|
await page.keyboard.up('Space')
|
|
await expect(item(page, 1)).toBeFocused()
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.down('Space')
|
|
await page.keyboard.down('Space')
|
|
expect(await page.evaluate(() => window.selectorCalls)).toEqual([])
|
|
await page.keyboard.up('Space')
|
|
await expect(trigger(page)).toBeFocused()
|
|
expect(await page.evaluate(() => window.selectorCalls)).toEqual([[true, 'Charlie', true, 'click']])
|
|
expect(await page.evaluate(() => window.selectorClicks)).toEqual([[true, true, 'click'], [true, true, 'click']])
|
|
await page.keyboard.press('ArrowDown')
|
|
await expect(item(page, 2)).toBeFocused()
|
|
await page.keyboard.press('Escape')
|
|
await expect(trigger(page)).toBeFocused()
|
|
await expect(list(page)).toBeHidden()
|
|
expect(await page.evaluate(() => window.art.fullscreenWeb)).toBe(true)
|
|
expect(await page.evaluate(() => window.selectorHotkeys)).toEqual([])
|
|
await page.keyboard.press('Tab')
|
|
await expect(trigger(page)).not.toBeFocused()
|
|
await expect(list(page)).toBeHidden()
|
|
})
|
|
|
|
test('candidate: keyboard selector retains asynchronous choice ownership and disposal', async ({ page }) => {
|
|
await setup(page, 'async')
|
|
await trigger(page).focus()
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('Home')
|
|
await page.keyboard.press('Enter')
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('End')
|
|
await page.keyboard.press('Enter')
|
|
await page.evaluate(() => {
|
|
window.pendingSelections.Hotel('Latest')
|
|
window.pendingSelections.Alpha('Obsolete')
|
|
})
|
|
await expect(trigger(page)).toHaveText('Latest')
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('Home')
|
|
await page.keyboard.press('Enter')
|
|
await page.evaluate(() => {
|
|
window.detachedValue = window.art.controls['keyboard-selector'].querySelector('.art-selector-value')
|
|
window.art.controls.remove('keyboard-selector')
|
|
window.pendingSelections.Alpha('After removal')
|
|
})
|
|
expect(await page.evaluate(() => window.detachedValue.textContent)).toBe('Alpha')
|
|
expect(await page.evaluate(() => {
|
|
const event = new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true, cancelable: true })
|
|
window.detachedValue.dispatchEvent(event)
|
|
return event.defaultPrevented
|
|
})).toBe(false)
|
|
})
|
|
|
|
test('candidate: pointer hover and selector selection retain their existing click behavior', async ({ page }) => {
|
|
await setup(page)
|
|
await page.locator('#play').focus()
|
|
await trigger(page).hover()
|
|
await expect(list(page)).toBeVisible()
|
|
await expect(trigger(page)).toHaveAttribute('aria-expanded', 'true')
|
|
await item(page, 0).click()
|
|
await expect(trigger(page)).toHaveText('Alpha')
|
|
await expect(item(page, 0)).toHaveAttribute('aria-selected', 'true')
|
|
expect(await page.evaluate(() => window.selectorCalls)).toEqual([[true, 'Alpha', true, 'click']])
|
|
await trigger(page).focus()
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('Escape')
|
|
await expect(list(page)).toBeHidden()
|
|
await page.locator('#play').hover()
|
|
await trigger(page).hover()
|
|
await expect(list(page)).toBeVisible()
|
|
await expect(trigger(page)).toHaveAttribute('aria-expanded', 'true')
|
|
})
|
|
|
|
test('candidate: custom native controls retain their semantics and typing inside selector content', async ({ page }) => {
|
|
await setup(page, 'complex')
|
|
await trigger(page).focus()
|
|
await page.keyboard.press('ArrowDown')
|
|
await expect(page.locator('#nested-option')).toBeFocused()
|
|
await expect(list(page)).toHaveAttribute('role', 'group')
|
|
expect(await item(page, 0).getAttribute('role')).toBeNull()
|
|
await page.keyboard.press('Space')
|
|
expect(await page.evaluate(() => window.selectorCalls)).toEqual([[true, 'native', true, 'click']])
|
|
const currentButton = trigger(page).locator('#nested-option')
|
|
await expect(currentButton).toBeFocused()
|
|
expect(await trigger(page).getAttribute('role')).toBeNull()
|
|
expect(await currentButton.getAttribute('aria-haspopup')).toBeNull()
|
|
expect(await page.evaluate(() => window.art.video.paused)).toBe(true)
|
|
await page.keyboard.press('ArrowDown')
|
|
await expect(list(page).locator('#nested-option')).toBeFocused()
|
|
await page.locator('.art-selector-list #nested-input').focus()
|
|
await page.keyboard.press('End')
|
|
await page.keyboard.type(' text')
|
|
await expect(page.locator('.art-selector-list #nested-input')).toHaveValue('Editable text')
|
|
await page.keyboard.press('Escape')
|
|
await expect(list(page)).toBeHidden()
|
|
expect(await page.evaluate(() => window.selectorHotkeys)).toEqual([])
|
|
})
|
|
|
|
test('candidate: empty selectors are disabled and selection callbacks keep explicitly moved focus', async ({ page }) => {
|
|
await setup(page, 'empty')
|
|
await trigger(page).focus()
|
|
await expect(trigger(page)).toHaveAttribute('aria-disabled', 'true')
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('Enter')
|
|
await expect(trigger(page)).toHaveAttribute('aria-expanded', 'false')
|
|
await expect(trigger(page)).toBeFocused()
|
|
expect(await page.evaluate(() => window.selectorClicks)).toEqual([])
|
|
await setup(page, 'external-focus')
|
|
await trigger(page).focus()
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('Enter')
|
|
await expect(page.locator('#play')).toBeFocused()
|
|
await expect(list(page)).toBeHidden()
|
|
})
|
|
|
|
test('candidate: typeahead accepts prefixes, resets after navigation and leaves modified input alone', async ({ page }) => {
|
|
await setup(page)
|
|
await trigger(page).focus()
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.type('ho')
|
|
await expect(item(page, 7)).toBeFocused()
|
|
await page.keyboard.press('Home')
|
|
await page.keyboard.press('f')
|
|
await expect(item(page, 5)).toBeFocused()
|
|
await page.keyboard.press('ArrowUp')
|
|
await page.keyboard.press('h')
|
|
await expect(item(page, 7)).toBeFocused()
|
|
await page.keyboard.press('Control+Home')
|
|
await expect(item(page, 7)).toBeFocused()
|
|
expect(await page.evaluate(() => window.selectorCalls)).toEqual([])
|
|
})
|
|
|
|
test('candidate: native trigger markup remains a single button and can open the existing popup', async ({ page }) => {
|
|
await setup(page)
|
|
await page.evaluate(() => {
|
|
window.art.controls.update({ name: 'keyboard-selector', html: '<button id="custom-trigger">Choose quality</button>' })
|
|
})
|
|
const button = page.locator('#custom-trigger')
|
|
await expect(button).toHaveAttribute('aria-haspopup', 'listbox')
|
|
expect(await trigger(page).getAttribute('role')).toBeNull()
|
|
await button.focus()
|
|
await page.keyboard.press('Space')
|
|
await expect(item(page, 1)).toBeFocused()
|
|
expect(await page.evaluate(() => window.selectorClicks)).toEqual([[true, true, 'click']])
|
|
expect(await page.evaluate(() => window.art.video.paused)).toBe(true)
|
|
await page.keyboard.press('Escape')
|
|
await expect(button).toBeFocused()
|
|
await expect(list(page)).toBeHidden()
|
|
})
|
|
|
|
test('candidate: a dismissed hovered selector releases the next Escape to fullscreen', async ({ page }) => {
|
|
await setup(page)
|
|
await page.evaluate(() => {
|
|
window.art.fullscreenWeb = true
|
|
})
|
|
await trigger(page).hover()
|
|
await trigger(page).focus()
|
|
await page.keyboard.press('ArrowDown')
|
|
await page.keyboard.press('Escape')
|
|
await expect(list(page)).toBeHidden()
|
|
expect(await page.evaluate(() => window.art.fullscreenWeb)).toBe(true)
|
|
await page.keyboard.press('Escape')
|
|
await expect.poll(() => page.evaluate(() => window.art.fullscreenWeb)).toBe(false)
|
|
})
|
|
|
|
test('candidate: hovering an empty selector does not announce an expanded popup', async ({ page }) => {
|
|
await setup(page, 'empty')
|
|
await trigger(page).hover()
|
|
await expect(trigger(page)).toHaveAttribute('aria-expanded', 'false')
|
|
await expect(trigger(page)).toHaveAttribute('aria-disabled', 'true')
|
|
})
|