mirror of
https://github.com/zoriya/v10.git
synced 2026-08-05 05:37:21 +00:00
fix(react): address stale media reference (#1677)
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
|
||||
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react';
|
||||
import { CAPTIONS_OFF_VALUE } from '@videojs/core';
|
||||
import type { ReactNode } from 'react';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { PlayerContextProvider, type PlayerContextValue } from '../../../player/context';
|
||||
import { createPlayerWrapper } from '../../../testing/mocks';
|
||||
import { Menu } from '../../menu';
|
||||
import { useCaptionsOptions } from '../use-captions-options';
|
||||
@@ -45,6 +46,42 @@ function renderCaptionsMenu({
|
||||
return { selectSubtitlesTrack };
|
||||
}
|
||||
|
||||
function createReactiveTextTrackWrapper(initialState: Record<string, unknown>) {
|
||||
const listeners = new Set<() => void>();
|
||||
const store = {
|
||||
state: initialState,
|
||||
subscribe: (callback: () => void) => {
|
||||
listeners.add(callback);
|
||||
return () => listeners.delete(callback);
|
||||
},
|
||||
attach: vi.fn(() => vi.fn()),
|
||||
destroy: vi.fn(),
|
||||
};
|
||||
|
||||
const value: PlayerContextValue = {
|
||||
store: store as unknown as PlayerContextValue['store'],
|
||||
media: null,
|
||||
setMedia: vi.fn(),
|
||||
container: null,
|
||||
setContainer: vi.fn(),
|
||||
};
|
||||
|
||||
return {
|
||||
updateState(next: Record<string, unknown>) {
|
||||
store.state = next;
|
||||
for (const listener of listeners) listener();
|
||||
},
|
||||
Wrapper({ children }: { children: ReactNode }) {
|
||||
return <PlayerContextProvider value={value}>{children}</PlayerContextProvider>;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function CaptionsAvailability(): ReactNode {
|
||||
const captions = useCaptionsOptions();
|
||||
return <div data-testid="availability">{captions?.state.availability ?? 'missing'}</div>;
|
||||
}
|
||||
|
||||
function CaptionsRadioGroup(): ReactNode {
|
||||
const captions = useCaptionsOptions();
|
||||
if (!captions?.showMenu) return null;
|
||||
@@ -94,4 +131,37 @@ describe('useCaptionsOptions', () => {
|
||||
|
||||
expect(selectSubtitlesTrack).toHaveBeenCalledWith(CAPTIONS_OFF_VALUE);
|
||||
});
|
||||
|
||||
it('updates when caption tracks become available', () => {
|
||||
const { Wrapper, updateState } = createReactiveTextTrackWrapper({
|
||||
chaptersCues: [],
|
||||
thumbnailCues: [],
|
||||
thumbnailTrackSrc: null,
|
||||
textTrackList: [],
|
||||
subtitlesShowing: false,
|
||||
selectSubtitlesTrack: vi.fn(),
|
||||
toggleSubtitles: vi.fn(),
|
||||
});
|
||||
|
||||
render(<CaptionsAvailability />, { wrapper: Wrapper });
|
||||
|
||||
expect(screen.getByTestId('availability').textContent).toBe('unavailable');
|
||||
|
||||
act(() => {
|
||||
updateState({
|
||||
chaptersCues: [],
|
||||
thumbnailCues: [],
|
||||
thumbnailTrackSrc: null,
|
||||
textTrackList: [
|
||||
{ kind: 'subtitles', label: 'English', language: 'en', mode: 'disabled' },
|
||||
{ kind: 'subtitles', label: 'Spanish', language: 'es', mode: 'showing' },
|
||||
],
|
||||
subtitlesShowing: true,
|
||||
selectSubtitlesTrack: vi.fn(),
|
||||
toggleSubtitles: vi.fn(),
|
||||
});
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('availability').textContent).toBe('available');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import {
|
||||
CAPTIONS_OFF_VALUE,
|
||||
type CaptionsRadioGroupCore,
|
||||
CaptionsRadioGroupCore as CaptionsRadioGroupCoreClass,
|
||||
} from '@videojs/core';
|
||||
import { CAPTIONS_OFF_VALUE, CaptionsRadioGroupCore } from '@videojs/core';
|
||||
import { logMissingFeature, selectTextTrack } from '@videojs/core/dom';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
@@ -28,8 +24,10 @@ export interface CaptionsOptionsResult {
|
||||
}
|
||||
|
||||
export function useCaptionsOptions(props?: CaptionsOptionsProps): CaptionsOptionsResult | null {
|
||||
'use no memo';
|
||||
|
||||
const media = usePlayer(selectTextTrack);
|
||||
const [core] = useState(() => new CaptionsRadioGroupCoreClass());
|
||||
const [core] = useState(() => new CaptionsRadioGroupCore());
|
||||
|
||||
core.setProps(props ?? {});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user