mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-19 19:42:20 -08:00
refactor(core): [CORE-22] validate compatible core and defer initial layout
This commit is contained in:
1 parent
5efc7f7699
commit
adec20213f
24 files changed
+77377
-50
No files matched your search
@@ -84,6 +84,73 @@ test('candidate: setting navigation ignores hidden rows and inert descendants',
|
||||
await expect(row(page, 'quality')).toBeFocused()
|
||||
})
|
||||
|
||||
test('candidate: long setting panels observe live button availability on every navigation', async ({ page }, testInfo) => {
|
||||
await setup(page)
|
||||
await page.evaluate(() => {
|
||||
const setting = window.art.setting
|
||||
for (const name of ['quality', 'loop', 'offset', 'action'])
|
||||
setting.remove(name)
|
||||
for (let index = 0; index < 40; index++)
|
||||
setting.add({ name: `row-${index}`, html: `<button id="setting-button-${index}">Action ${index}</button>` })
|
||||
})
|
||||
await page.keyboard.press('Enter')
|
||||
await expect(page.locator('#setting-button-0')).toBeFocused()
|
||||
const measurements = []
|
||||
async function navigate(key, expected) {
|
||||
await page.evaluate(() => {
|
||||
const panel = window.art.setting.cache.get(window.art.setting.active)
|
||||
const query = panel.querySelectorAll
|
||||
const style = window.getComputedStyle
|
||||
const counts = { queries: 0, styles: 0 }
|
||||
panel.querySelectorAll = function (...args) {
|
||||
counts.queries++
|
||||
return query.apply(this, args)
|
||||
}
|
||||
window.getComputedStyle = function (element, ...args) {
|
||||
if (panel.contains(element))
|
||||
counts.styles++
|
||||
return style.call(this, element, ...args)
|
||||
}
|
||||
window.finishSettingMeasurement = () => {
|
||||
delete panel.querySelectorAll
|
||||
window.getComputedStyle = style
|
||||
delete window.finishSettingMeasurement
|
||||
return counts
|
||||
}
|
||||
})
|
||||
try {
|
||||
await page.keyboard.press(key)
|
||||
}
|
||||
finally {
|
||||
measurements.push({ key, expected, ...await page.evaluate(() => window.finishSettingMeasurement()) })
|
||||
}
|
||||
await expect(page.locator(`#setting-button-${expected}`)).toBeFocused()
|
||||
}
|
||||
await navigate('ArrowDown', 1)
|
||||
await page.evaluate(() => {
|
||||
document.querySelector('#setting-button-2').style.visibility = 'hidden'
|
||||
document.querySelector('#setting-button-3').closest('.art-setting-item').setAttribute('inert', '')
|
||||
document.querySelector('#setting-button-4').disabled = true
|
||||
document.querySelector('#setting-button-5').tabIndex = -1
|
||||
})
|
||||
await navigate('ArrowDown', 6)
|
||||
await navigate('a', 6)
|
||||
await navigate('End', 39)
|
||||
await page.evaluate(() => {
|
||||
document.querySelector('#setting-button-2').style.visibility = ''
|
||||
document.querySelector('#setting-button-3').closest('.art-setting-item').removeAttribute('inert')
|
||||
document.querySelector('#setting-button-4').disabled = false
|
||||
document.querySelector('#setting-button-5').removeAttribute('tabindex')
|
||||
window.art.setting.remove('row-1')
|
||||
})
|
||||
await navigate('Home', 0)
|
||||
await navigate('ArrowDown', 2)
|
||||
await navigate('ArrowDown', 3)
|
||||
await navigate('ArrowDown', 4)
|
||||
await navigate('ArrowDown', 5)
|
||||
await testInfo.attach('setting-navigation-work', { contentType: 'application/json', body: JSON.stringify(measurements, null, 2) })
|
||||
})
|
||||
|
||||
test('candidate: setting keyboard enters nested panels and returns to the originating item', async ({ page }, testInfo) => {
|
||||
await setup(page)
|
||||
await page.evaluate(() => {
|
||||
|
||||
@@ -1,5 +1,57 @@
|
||||
import { expect, test } from './fixtures.js'
|
||||
|
||||
for (const fallback of [false, true]) {
|
||||
test(`candidate: initial control measurement uses the browser layout phase with fallback=${fallback}`, async ({ page }, testInfo) => {
|
||||
if (fallback)
|
||||
await page.addInitScript(() => { window.ResizeObserver = undefined })
|
||||
await page.goto('/test/player.html?core=candidate&chapter=published')
|
||||
const reads = await page.evaluate(() => {
|
||||
document.querySelector('.player').style.width = '320px'
|
||||
const descriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight')
|
||||
let reads = 0
|
||||
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', {
|
||||
...descriptor,
|
||||
get() {
|
||||
if (this.classList.contains('art-controls'))
|
||||
reads++
|
||||
return descriptor.get.call(this)
|
||||
},
|
||||
})
|
||||
try {
|
||||
window.art = new window.Artplayer({ container: '.player', url: '', setting: true, fullscreenWeb: true, controls: [{ name: 'probe', position: 'right', html: 'Probe control' }] })
|
||||
}
|
||||
finally {
|
||||
Object.defineProperty(HTMLElement.prototype, 'offsetHeight', descriptor)
|
||||
}
|
||||
return reads
|
||||
})
|
||||
await testInfo.attach('constructor-control-layout-reads', { contentType: 'application/json', body: JSON.stringify({ fallback, reads }) })
|
||||
expect(reads).toBe(fallback ? 1 : 0)
|
||||
const measured = await page.evaluate(async () => {
|
||||
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))
|
||||
const { $controls, $player } = window.art.template
|
||||
return { height: $controls.offsetHeight, recorded: Number.parseFloat($player.style.getPropertyValue('--art-controls-height')) }
|
||||
})
|
||||
expect(measured.height).toBe(92)
|
||||
expect(measured.recorded).toBe(measured.height)
|
||||
await expect.poll(() => page.evaluate(() => Number.parseFloat(getComputedStyle(window.art.template.$subtitle).bottom))).toBeGreaterThanOrEqual(measured.height)
|
||||
})
|
||||
}
|
||||
|
||||
test('candidate: destroying before initial control observation leaves no late layout write', async ({ page }) => {
|
||||
await page.goto('/test/player.html?core=candidate&chapter=published')
|
||||
const state = await page.evaluate(async () => {
|
||||
const art = new window.Artplayer({ container: '.player', url: '' })
|
||||
const player = art.template.$player
|
||||
art.destroy(false)
|
||||
const before = player.getAttribute('style')
|
||||
await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))
|
||||
return { before, after: player.getAttribute('style'), instances: window.Artplayer.instances.length }
|
||||
})
|
||||
expect(state.after).toBe(state.before)
|
||||
expect(state.instances).toBe(0)
|
||||
})
|
||||
|
||||
for (const fallback of [false, true]) {
|
||||
test(`candidate: control layout follows resize and removal with observer fallback=${fallback}`, async ({ page }) => {
|
||||
if (fallback) {
|
||||
|
||||
Reference in new issue
Block a user