ci / module boundaries / boundaries (push) Successful in 6s
ci / api / api (push) Successful in 47s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m2s
ci / db / db (push) Successful in 1m4s
ci / docker / docker (образы собираются — их сервер собирает сам при обновлении) (push) Successful in 1m30s
ci / ts / ts (packages + web) (push) Successful in 2m54s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m14s
73 lines
3.4 KiB
TypeScript
73 lines
3.4 KiB
TypeScript
import type { AuthSession, Track } from '@pipu/core';
|
|
import { beforeEach, expect, it, vi } from 'vitest';
|
|
|
|
vi.mock('@react-native-community/netinfo', () => ({ default: {
|
|
fetch: async () => ({ type: 'wifi' }), addEventListener: () => () => {},
|
|
} }));
|
|
vi.mock('../src/lib/prefs', () => ({
|
|
qualityPrefs: { load: async () => ({ wifi: 'high', cellular: 'medium' }) },
|
|
playerCache: { clearIfRequested: async () => {} },
|
|
}));
|
|
vi.mock('../src/offline/events', () => ({ PersistentEvents: class {} }));
|
|
vi.mock('../src/offline/downloads', () => ({
|
|
downloads: { isDownloaded: () => false, localTrack: () => undefined, localCover: () => undefined, downloaded: () => [] },
|
|
sizedCover: (s: string) => s,
|
|
}));
|
|
vi.mock('react-native-track-player', () => ({
|
|
default: {
|
|
isServiceRunning: async () => true, setupPlayer: async () => {}, updateOptions: async () => {},
|
|
addEventListener: vi.fn(), reset: vi.fn().mockResolvedValue(undefined),
|
|
add: vi.fn().mockResolvedValue(undefined), play: vi.fn().mockResolvedValue(undefined),
|
|
skip: vi.fn().mockResolvedValue(undefined),
|
|
},
|
|
AppKilledPlaybackBehavior: {}, Capability: {}, Event: {}, State: {},
|
|
}));
|
|
|
|
function track(id: string): Track {
|
|
return { id, title: id, duration_ms: 1000, artists: [],
|
|
album: { id: 'album', title: 'Album', year: null, cover_url: null, cover_colors: [] },
|
|
genres: [], disc_no: 1, track_no: 1, available: true, stream_url: `/s/${id}?sig=x`, reaction: null, features: null };
|
|
}
|
|
function deferred<T>() {
|
|
let resolve!: (v: T) => void;
|
|
const promise = new Promise<T>((yes) => { resolve = yes; });
|
|
return { promise, resolve };
|
|
}
|
|
const reply = (id: string) => ({ data: { session_id: id, activity: null, mood: null, fallback: false, tracks: [track(id)] }, response: new Response() });
|
|
|
|
beforeEach(() => { vi.resetModules(); vi.clearAllMocks(); });
|
|
|
|
it('ignores a wave creation response after the player is reset', async () => {
|
|
const { player } = await import('../src/player/controller');
|
|
const native = (await import('react-native-track-player')).default;
|
|
const pending = deferred<ReturnType<typeof reply>>();
|
|
const post = vi.fn().mockReturnValue(pending.promise);
|
|
player.attach({ client: { POST: post } } as unknown as AuthSession, 'http://localhost', null);
|
|
const start = player.startWave(null, null);
|
|
await vi.waitFor(() => expect(post).toHaveBeenCalled());
|
|
await player.reset();
|
|
pending.resolve(reply('old'));
|
|
await start;
|
|
expect(player.getState().wave).toBeNull();
|
|
expect(player.getState().queue).toEqual([]);
|
|
expect(native.add).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('does not let a late context response replace a manually selected playlist', async () => {
|
|
const { player } = await import('../src/player/controller');
|
|
const pending = deferred<ReturnType<typeof reply>>();
|
|
const session = { client: {
|
|
POST: vi.fn().mockResolvedValue(reply('wave')),
|
|
PATCH: vi.fn().mockReturnValue(pending.promise), DELETE: vi.fn().mockResolvedValue({}),
|
|
} };
|
|
player.attach(session as unknown as AuthSession, 'http://localhost', null);
|
|
await player.startWave(null, null);
|
|
const change = player.setWaveContext('focus', null);
|
|
await player.playList([track('manual')], 0, { type: 'album', id: 'album' });
|
|
pending.resolve(reply('stale'));
|
|
await change;
|
|
expect(player.getState().wave).toBeNull();
|
|
expect(player.getState().queue.map((t) => t.id)).toEqual(['manual']);
|
|
expect(player.getState().context?.type).toBe('album');
|
|
});
|