Feature: Feature and preset reference — E2E tests + implementation (#1248)

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Darius Cepulis
2026-04-07 09:24:23 -05:00
committed by GitHub
co-authored by Claude Opus 4.6
parent d2c43db550
commit a4d8e2a255
26 changed files with 1112 additions and 1 deletions
@@ -0,0 +1,55 @@
/*
* Feature state interface fixtures.
*
* Exercises: property extraction (state), method extraction (actions),
* JSDoc description flow-through, type alias resolution (MediaFeatureAvailability),
* method parameter types, method return types, Promise return types.
*/
export interface MediaPlaybackState {
/**
* Whether playback is paused.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/paused
*/
paused: boolean;
/**
* Whether playback has reached the end.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/ended
*/
ended: boolean;
/**
* Start playback.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play
*/
play(): Promise<void>;
/** Pause playback. */
pause(): void;
}
/** Indicates whether a feature can be programmatically controlled on this platform. */
export type MediaFeatureAvailability = 'available' | 'unavailable' | 'unsupported';
/** Controls audio volume and mute state. */
export interface MediaVolumeState {
/**
* Volume level from 0 (silent) to 1 (max).
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volume
*/
volume: number;
/** Whether audio is muted. */
muted: boolean;
/** Whether volume can be programmatically set on this platform. */
volumeAvailability: MediaFeatureAvailability;
/**
* Set volume (clamped 0-1). Returns the clamped value.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/volume
*/
setVolume(volume: number): number;
/** Toggle mute state. Returns the new muted value. */
toggleMuted(): boolean;
}
@@ -0,0 +1,9 @@
/**
* Mock definePlayerFeature — identity function matching the real signature.
* The builder only needs the TypeScript types to resolve; it never runs this.
*/
export const definePlayerFeature = <State>(config: {
name?: string;
state: (ctx: any) => State;
attach?: (ctx: any) => void;
}) => config;
@@ -0,0 +1,8 @@
/**
* Short alias re-exports for features.
*
* Exercises: namespace re-export filtering — `export * as features from './feature.parts'`
* in the index should NOT produce a feature entry named "features".
*/
export { playbackFeature as playback } from './playback';
export { volumeFeature as volume } from './volume';
@@ -0,0 +1,12 @@
/**
* Features index fixture.
*
* Exercises: feature discovery filters singular *Feature exports, ignores
* plural *Features (feature bundles) from presets, and ignores namespace
* re-exports (export * as features).
*/
export * as features from './feature.parts';
export * from './playback';
export * from './presets';
export * from './volume';
@@ -0,0 +1,20 @@
/**
* Mock playback feature.
*
* Exercises: simple boolean state properties, void and Promise<void> action methods,
* JSDoc description extraction from the state interface.
*/
import type { MediaPlaybackState } from '../../../core/media/state';
import { definePlayerFeature } from '../../feature';
export const playbackFeature = definePlayerFeature({
name: 'playback',
state: (): MediaPlaybackState => ({
paused: true,
ended: false,
play() {
return Promise.resolve();
},
pause() {},
}),
});
@@ -0,0 +1,13 @@
/**
* Mock feature bundles.
*
* Exercises: feature bundle arrays (plural *Features naming), feature list
* resolution from array elements. videoFeatures has both features,
* audioFeatures has only playback.
*/
import { playbackFeature } from './playback';
import { volumeFeature } from './volume';
export const videoFeatures = [playbackFeature, volumeFeature];
export const audioFeatures = [playbackFeature];
@@ -0,0 +1,23 @@
/**
* Mock volume feature.
*
* Exercises: numeric state property, type alias (MediaFeatureAvailability),
* methods with parameters and return values, boolean state property.
*/
import type { MediaVolumeState } from '../../../core/media/state';
import { definePlayerFeature } from '../../feature';
export const volumeFeature = definePlayerFeature({
name: 'volume',
state: (): MediaVolumeState => ({
volume: 1,
muted: false,
volumeAvailability: 'available',
setVolume(_volume: number) {
return 1;
},
toggleMuted() {
return false;
},
}),
});
@@ -0,0 +1,10 @@
/**
* Mock HTML audio skin element.
*
* Exercises: single skin per preset, skin detection via SkinElement inheritance.
*/
import { SkinElement } from '../skin-element';
export class AudioSkinElement extends SkinElement {
static readonly tagName = 'audio-skin';
}
@@ -0,0 +1,11 @@
/**
* Mock SkinElement base class.
*
* The builder detects HTML skins by checking if a class extends SkinElement.
* This fixture provides the base class for that inheritance check.
*/
export class SkinElement {
static shadowRootOptions: any;
static styles?: any;
static template?: any;
}
@@ -0,0 +1,10 @@
/**
* Mock HTML minimal video skin element.
*
* Exercises: multiple skins per preset, skin detection via SkinElement inheritance.
*/
import { SkinElement } from '../skin-element';
export class MinimalVideoSkinElement extends SkinElement {
static readonly tagName = 'video-minimal-skin';
}
@@ -0,0 +1,10 @@
/**
* Mock HTML video tailwind skin element.
*
* Exercises: tailwind skin exclusion — this should NOT appear in the output.
*/
import { SkinElement } from '../skin-element';
export class VideoSkinTailwindElement extends SkinElement {
static readonly tagName = 'video-skin-tailwind';
}
@@ -0,0 +1,10 @@
/**
* Mock HTML video skin element.
*
* Exercises: skin detection via SkinElement inheritance, tagName extraction.
*/
import { SkinElement } from '../skin-element';
export class VideoSkinElement extends SkinElement {
static readonly tagName = 'video-skin';
}
@@ -0,0 +1,7 @@
/**
* Mock HTML audio preset.
*
* Exercises: preset with fewer features and a single skin.
*/
export { audioFeatures } from '../../../core/src/dom/store/features/presets';
export { AudioSkinElement } from '../define/audio/skin';
@@ -0,0 +1,11 @@
/**
* Mock HTML video preset.
*
* Exercises: preset discovery, feature bundle export, skin exports,
* tailwind skin exclusion. HTML presets do NOT export media elements
* (the native <video> is implied by the preset name).
*/
export { videoFeatures } from '../../../core/src/dom/store/features/presets';
export { MinimalVideoSkinElement } from '../define/video/minimal-skin';
export { VideoSkinElement } from '../define/video/skin';
export { VideoSkinTailwindElement } from '../define/video/skin.tailwind';
@@ -0,0 +1,4 @@
/**
* Mock React Audio media element.
*/
export function Audio(): void {}
@@ -0,0 +1,7 @@
/**
* Mock React Video media element.
*
* Exercises: media element detection in React presets. Media elements are
* exports that are not feature bundles (*Features) and not skins (*Skin).
*/
export function Video(): void {}
@@ -0,0 +1,8 @@
/**
* Mock React audio preset.
*
* Exercises: preset with fewer features, single skin, different media element.
*/
export { audioFeatures } from '../../../../core/src/dom/store/features/presets';
export { Audio } from '../../media/audio';
export { AudioSkin } from './skin';
@@ -0,0 +1,4 @@
/**
* Mock React AudioSkin component.
*/
export function AudioSkin(): void {}
@@ -0,0 +1,11 @@
/**
* Mock React video preset.
*
* Exercises: preset discovery, feature bundle export, skin exports (named),
* media element export, tailwind skin exclusion.
*/
export { videoFeatures } from '../../../../core/src/dom/store/features/presets';
export { Video } from '../../media/video';
export { MinimalVideoSkin } from './minimal-skin';
export { VideoSkin } from './skin';
export { VideoSkinTailwind } from './skin.tailwind';
@@ -0,0 +1,6 @@
/**
* Mock React MinimalVideoSkin component.
*
* Exercises: multiple skins per preset.
*/
export function MinimalVideoSkin(): void {}
@@ -0,0 +1,6 @@
/**
* Mock React VideoSkinTailwind component.
*
* Exercises: tailwind skin exclusion this should NOT appear in the output.
*/
export function VideoSkinTailwind(): void {}
@@ -0,0 +1,6 @@
/**
* Mock React VideoSkin component.
*
* Exercises: React skin detection via *Skin naming convention.
*/
export function VideoSkin(): void {}