web-player: add Vitest, unit tests and pure queue operations

This commit is contained in:
2026-09-24 04:54:09 +02:00
parent 87c3f97529
commit b2f450dcf7
12 changed files with 586 additions and 87 deletions
@@ -0,0 +1,23 @@
import { describe, expect, it } from 'vitest';
import { appendUnique, shuffle } from './collections';
describe('appendUnique', () => {
const keyOf = (item: { id: number }) => item.id;
it('appends only unseen keys', () => {
const result = appendUnique([{ id: 1 }], [{ id: 1 }, { id: 2 }, { id: 2 }, { id: 3 }], keyOf);
expect(result).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }]);
});
it('returns the same array when nothing is new', () => {
const current = [{ id: 1 }];
expect(appendUnique(current, [{ id: 1 }], keyOf)).toBe(current);
});
});
describe('shuffle', () => {
it('keeps the same elements and does not mutate the input', () => {
const input = [1, 2, 3, 4, 5, 6];
const result = shuffle(input);
expect([...result].sort()).toEqual(input);
expect(input).toEqual([1, 2, 3, 4, 5, 6]);
});
});
+28
View File
@@ -0,0 +1,28 @@
import { describe, expect, it } from 'vitest';
import { findConflict, genreHue } from './genres';
describe('genreHue', () => {
it('is stable and within 0-359', () => {
for (const genre of ['rock', 'something-unknown', 'ñandú']) {
const hue = genreHue(genre);
expect(hue).toBe(genreHue(genre));
expect(hue).toBeGreaterThanOrEqual(0);
expect(hue).toBeLessThan(360);
}
});
});
describe('findConflict', () => {
const options = [
{ genre: 'a', incompatibleWith: ['b'] },
{ genre: 'b', incompatibleWith: ['a'] },
{ genre: 'c', incompatibleWith: [] }
];
it('returns the first incompatible pair', () => {
expect(findConflict(['c', 'a', 'b'], options)).toEqual(['a', 'b']);
});
it('returns undefined when compatible or unknown', () => {
expect(findConflict(['a', 'c'], options)).toBeUndefined();
expect(findConflict(['x', 'y'], options)).toBeUndefined();
});
});
+28
View File
@@ -0,0 +1,28 @@
import { describe, expect, it } from 'vitest';
import { findTopResult } from './search';
const empty = { tracks: [], albums: [], playlists: [], users: [] };
describe('findTopResult', () => {
it('prefers prefix matches by kind: user, track, album, playlist', () => {
const result = findTopResult({
query: 'Ab',
users: [{ name: 'Zed' }, { name: 'abel' }],
tracks: [{ title: 'Abc' }],
albums: [{ title: 'Abd' }],
playlists: [{ name: 'Abe' }]
});
expect(result).toEqual({ kind: 'user', user: { name: 'abel' } });
});
it('matches case-insensitively and ignores surrounding spaces', () => {
const result = findTopResult({ ...empty, query: ' SO ', tracks: [{ title: 'song' }] });
expect(result).toEqual({ kind: 'track', track: { title: 'song' } });
});
it('falls back to the first available result', () => {
const result = findTopResult({ ...empty, query: 'zzz', albums: [{ title: 'Other' }] });
expect(result).toEqual({ kind: 'album', album: { title: 'Other' } });
});
it('returns null without results', () => {
expect(findTopResult({ ...empty, query: 'x' })).toBeNull();
});
});