Files
SubMiner/src/animeui/format.test.ts
T
sudacode e64ff1a0ee feat(anime): add anime browser powered by Aniyomi extensions
- Add `subminer anime` / `--anime` and a tray entry to open a browser that searches installed Aniyomi extension sources, shows cover art and episodes, and plays into mpv with overlay/mining attached
- Add an Extensions tab to add repos and install/update/remove sources, and per-source settings for sources needing config
- Support searching all sources at once with streaming, per-source results and status
- Prefer Japanese audio/subtitle tracks from the source and keep the primary subtitle slot reserved for Japanese
- Fix window/tray/Dock handling so the browser and mpv can be switched between without quitting the app or losing the Dock icon
- Add anime.repos, anime.extensionsDir, anime.preferredQuality config keys (no bundled repos or discovery)
2026-07-31 17:21:12 -07:00

61 lines
2.0 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { describeInstalled, sourceOptionLabel, summarizeSearch } from './format';
import type { AnimeBrowserSearchResult } from '../types/anime-browser';
const result = (
entryCount: number,
failures: AnimeBrowserSearchResult['failures'] = [],
): AnimeBrowserSearchResult => ({
entries: Array.from({ length: entryCount }, (_unused, index) => ({
url: `/a/${index}`,
title: `Anime ${index}`,
thumbnailUrl: null,
sourceId: 's',
sourceName: 'Source',
})),
hasNextPage: false,
failures,
});
test('sourceOptionLabel omits the language for an all-language source', () => {
assert.equal(sourceOptionLabel({ id: '1', name: 'Nyaa', lang: 'ja', pkg: 'p' }), 'Nyaa (ja)');
assert.equal(sourceOptionLabel({ id: '2', name: 'Jellyfin', lang: 'all', pkg: 'p' }), 'Jellyfin');
});
test('summarizeSearch counts results and singularizes one', () => {
assert.equal(summarizeSearch(result(4)), '4 results');
assert.equal(summarizeSearch(result(1)), '1 result');
assert.equal(summarizeSearch(result(0)), '0 results');
});
test('summarizeSearch names the sources that failed alongside the ones that answered', () => {
const summary = summarizeSearch(
result(6, [
{ sourceId: 'a', sourceName: 'Alpha', error: 'login required' },
{ sourceId: 'b', sourceName: 'Beta', error: 'timed out' },
]),
);
assert.equal(summary, '6 results · 2 unavailable: Alpha, Beta');
});
test('describeInstalled reports sources and languages when the extension loaded', () => {
assert.equal(
describeInstalled({
pkg: 'multi',
name: 'One, Two',
langs: ['en', 'ja'],
sourceCount: 2,
error: null,
}),
'multi · 2 sources · en, ja',
);
});
test('describeInstalled falls back to the package alone when nothing loaded', () => {
assert.equal(
describeInstalled({ pkg: 'broken', name: 'broken', langs: [], sourceCount: 0, error: 'boom' }),
'broken',
);
});