mirror of
https://github.com/hectorqin/reader.git
synced 2026-11-11 05:20:54 +08:00
44 lines
4.5 KiB
TypeScript
44 lines
4.5 KiB
TypeScript
import { render } from '../src/shared/ui/render-root.ts';
|
|
// @vitest-environment jsdom
|
|
import {afterEach,expect,it,vi} from 'vitest';
|
|
import {act} from 'react';
|
|
|
|
import {VideoEpisodeRail} from '../src/features/media/components/video-episode-rail.tsx';
|
|
import {ArtistTracks} from '../src/features/media/components/artist-tracks.tsx';
|
|
import type {Detail,MediaApi} from '../src/features/media/api/media-api.ts';
|
|
import type {MediaPlayer} from '../src/features/media/services/player.ts';
|
|
import {readPlaybackPreferences,savePlaybackPreferences} from '../src/features/media/services/playback-preferences.ts';
|
|
const root=document.createElement('div');document.body.append(root);
|
|
afterEach(()=>{act(()=>render(null,root));localStorage.clear();});
|
|
const item={id:'ep1',kind:'episode',title:'第一集',parentId:'season1',libraryId:'lib',metadata:{},overrides:{},editions:[],children:[]} as Detail;
|
|
const episode=(id:string)=>({id,title:id,editions:[{id:'v-'+id,label:'原版',parts:[{id:'p-'+id,assetId:'a-'+id,title:id,start:0,end:60,available:true}]}]});
|
|
it('shows the full season for a direct episode and plays a selected episode without replacing metadata',async()=>{
|
|
const request=vi.fn().mockResolvedValue({episodes:[episode('ep1'),episode('ep2'),episode('ep3')]}),detail=vi.fn().mockResolvedValue({...item,id:'season1',kind:'season',title:'第一季',parentId:null}),play=vi.fn().mockResolvedValue(undefined);
|
|
await act(async()=>render(<VideoEpisodeRail api={{request,detail} as unknown as MediaApi} item={item} player={{currentPartId:'p-ep1',play} as unknown as MediaPlayer}/>,root));
|
|
await vi.waitFor(()=>expect(root.querySelectorAll('.media-video-episodes button')).toHaveLength(3));
|
|
expect(root.querySelector<HTMLButtonElement>('[aria-current=true]')!.disabled).toBe(true);
|
|
await act(async()=>root.querySelectorAll<HTMLButtonElement>('.media-video-episodes button')[1]!.click());
|
|
expect(play.mock.calls[0]![0].map((entry:{part:{id:string}})=>entry.part.id)).toEqual(['p-ep2','p-ep3']);
|
|
expect(request).toHaveBeenCalledWith('items/season1/season-playback','GET',undefined,expect.any(AbortSignal));
|
|
});
|
|
it('does not choose an ambiguous episode version automatically',async()=>{
|
|
const ep=episode('ep2');ep.editions.push({...ep.editions[0]!,id:'alternate',label:'配音版'});
|
|
const play=vi.fn(),api={request:vi.fn().mockResolvedValue({episodes:[ep]}),detail:vi.fn().mockResolvedValue({...item,id:'season1',parentId:null})} as unknown as MediaApi;
|
|
await act(async()=>render(<VideoEpisodeRail api={api} item={item} player={{currentPartId:'p-other',play} as unknown as MediaPlayer}/>,root));
|
|
await vi.waitFor(()=>expect(root.querySelector('.media-video-episodes button')).not.toBeNull());
|
|
await act(async()=>root.querySelector<HTMLButtonElement>('.media-video-episodes button')!.click());expect(play).not.toHaveBeenCalled();expect(root.querySelector('[role=status]')?.textContent).toContain('需要选择版本');
|
|
await act(async()=>{const select=root.querySelector<HTMLSelectElement>('[aria-label="ep2 版本"]')!;select.value='alternate';select.dispatchEvent(new Event('change',{bubbles:true}));});
|
|
await act(async()=>root.querySelector<HTMLButtonElement>('.media-video-episodes button')!.click());expect(play).toHaveBeenCalledOnce();
|
|
});
|
|
it('queries artist tracks inside the artist library, supports play and does not invent tracks',async()=>{
|
|
const song={...item,id:'song',kind:'track',title:'真实曲目',metadata:{artist:'北岸'}},items=vi.fn().mockResolvedValue({items:[song],total:1}),play=vi.fn().mockResolvedValue(undefined);
|
|
await act(async()=>render(<ArtistTracks api={{items} as unknown as MediaApi} artist={{...item,kind:'artist',title:'北岸'}} onPlay={play} onDetail={vi.fn()}/>,root));
|
|
expect(items).toHaveBeenCalledWith('lib','track','',0,expect.any(AbortSignal),'default',{artist:'北岸'});
|
|
await vi.waitFor(()=>expect(root.querySelector('[aria-label="播放 真实曲目"]')).not.toBeNull());
|
|
await act(async()=>root.querySelector<HTMLButtonElement>('[aria-label="播放 真实曲目"]')!.click());expect(play).toHaveBeenCalledWith('song');expect(root.querySelectorAll('.media-track-entry')).toHaveLength(1);
|
|
});
|
|
it('isolates playback preferences by account and validates invalid stored values',()=>{
|
|
savePlaybackPreferences('a',{defaultRate:1.5,continuous:false});expect(readPlaybackPreferences('a')).toEqual({defaultRate:1.5,continuous:false});expect(readPlaybackPreferences('b')).toEqual({defaultRate:1,continuous:true});
|
|
localStorage.setItem('reader.media.playback-preferences.v1:a','{"defaultRate":999,"continuous":"no"}');expect(readPlaybackPreferences('a')).toEqual({defaultRate:1,continuous:true});
|
|
});
|