mirror of
https://github.com/zoriya/v10.git
synced 2026-08-05 05:37:21 +00:00
test(sandbox): adapt playback rate E2E to menu UX (#1545)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9,6 +9,12 @@
|
||||
*
|
||||
* Each selector uses a CSS `,` (or) to match either renderer.
|
||||
*/
|
||||
|
||||
/** Toolbar: HTML wraps controls in `<media-controls>`, React in `<div class="media-controls">`. */
|
||||
function withinControls(selector: string): string {
|
||||
return `media-controls ${selector}, .media-controls ${selector}`;
|
||||
}
|
||||
|
||||
export const SELECTORS = {
|
||||
// Player containers
|
||||
// HTML: <video-player>, React: wrapper div around VideoSkin
|
||||
@@ -28,7 +34,20 @@ export const SELECTORS = {
|
||||
fullscreenButton: 'media-fullscreen-button, .media-button--fullscreen',
|
||||
pipButton: 'media-pip-button, .media-button--pip',
|
||||
captionsButton: 'media-captions-button, .media-button--captions',
|
||||
playbackRateButton: 'media-playback-rate-button, .media-button--playback-rate',
|
||||
playbackRateButton: [
|
||||
// Legacy cycle button / CSS skins (explicit class)
|
||||
withinControls('media-playback-rate-button'),
|
||||
withinControls('.media-button--playback-rate'),
|
||||
// HTML menu trigger — exclude nested settings-submenu rows (.media-menu__item)
|
||||
withinControls('media-playback-rate-menu-trigger:not(.media-menu__item)'),
|
||||
// Tailwind skins (utility classes only) + React menu triggers
|
||||
withinControls('button[aria-haspopup="menu"][aria-label^="Playback rate"]:not(.media-menu__item)'),
|
||||
].join(', '),
|
||||
/**
|
||||
* Open playback rate surface: trigger is also `data-rate`, so require `role="menu"` too
|
||||
* (covers HTML `<media-playback-rate-menu>` and React/Tailwind without `media-menu--playback-rate`).
|
||||
*/
|
||||
playbackRateMenuPanel: '[data-rate][role="menu"]',
|
||||
|
||||
// Sliders
|
||||
// HTML: <media-time-slider>, React: horizontal .media-slider inside .media-time-controls
|
||||
|
||||
@@ -185,6 +185,19 @@ export class PlayerPage {
|
||||
await this.page.mouse.move(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the playback rate menu and selects the first option that differs from the current rate.
|
||||
* Skins expose rate via a menu (not cycle-on-trigger).
|
||||
*/
|
||||
async selectAlternativePlaybackRate(): Promise<void> {
|
||||
await this.playbackRateButton.click();
|
||||
const option = this.page
|
||||
.locator(`${SELECTORS.playbackRateMenuPanel} [role="menuitemradio"][aria-checked="false"]`)
|
||||
.first();
|
||||
await expect(option).toBeVisible({ timeout: 5_000 });
|
||||
await option.click();
|
||||
}
|
||||
|
||||
/** Hover over the player area to trigger user-active state and show controls. */
|
||||
async showControls(): Promise<void> {
|
||||
// Use the play button as anchor — it's always inside the player
|
||||
|
||||
@@ -76,15 +76,13 @@ for (const { name, path, skipBrowsers } of AUDIO_PAGES as readonly PageEntry[])
|
||||
|
||||
// --- Playback Rate ---
|
||||
|
||||
test('playback rate button cycles rates', async () => {
|
||||
test('playback rate menu changes selected rate', async () => {
|
||||
const rateBtn = player.playbackRateButton;
|
||||
const initialRate = await rateBtn.getAttribute(DATA_ATTRS.rate);
|
||||
|
||||
await rateBtn.click();
|
||||
await player.page.waitForTimeout(200);
|
||||
await player.selectAlternativePlaybackRate();
|
||||
|
||||
const newRate = await rateBtn.getAttribute(DATA_ATTRS.rate);
|
||||
expect(newRate).not.toBe(initialRate);
|
||||
await expect.poll(async () => rateBtn.getAttribute(DATA_ATTRS.rate)).not.toBe(initialRate);
|
||||
});
|
||||
|
||||
// Audio skin does NOT have: fullscreen, PiP, captions, poster, storyboard
|
||||
|
||||
@@ -4,6 +4,8 @@ import { DATA_ATTRS, SELECTORS } from '../fixtures/selectors';
|
||||
import { PlayerPage } from '../page-objects/player';
|
||||
|
||||
for (const { name, path, media, skipBrowsers } of ALL_VIDEO_PAGES as readonly PageEntry[]) {
|
||||
/** Packaged skins use an inline rate control; CDN + site-ejected previews only expose nested settings menus. */
|
||||
const skipsInlinePlaybackRateMenuTest = path.includes('/cdn-video') || path.includes('/ejected');
|
||||
test.describe(`Video Controls — ${name}`, () => {
|
||||
test.skip(({ browserName }) => {
|
||||
return skipBrowsers?.includes(browserName as 'chromium' | 'webkit' | 'firefox') ?? false;
|
||||
@@ -91,15 +93,13 @@ for (const { name, path, media, skipBrowsers } of ALL_VIDEO_PAGES as readonly Pa
|
||||
|
||||
// --- Playback Rate ---
|
||||
|
||||
test('playback rate button cycles rates', async () => {
|
||||
(skipsInlinePlaybackRateMenuTest ? test.skip : test)('playback rate menu changes selected rate', async () => {
|
||||
const rateBtn = player.playbackRateButton;
|
||||
const initialRate = await rateBtn.getAttribute(DATA_ATTRS.rate);
|
||||
|
||||
await rateBtn.click();
|
||||
await player.page.waitForTimeout(200);
|
||||
await player.selectAlternativePlaybackRate();
|
||||
|
||||
const newRate = await rateBtn.getAttribute(DATA_ATTRS.rate);
|
||||
expect(newRate).not.toBe(initialRate);
|
||||
await expect.poll(async () => rateBtn.getAttribute(DATA_ATTRS.rate)).not.toBe(initialRate);
|
||||
});
|
||||
|
||||
// --- Poster ---
|
||||
|
||||
Reference in New Issue
Block a user