mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 22:45:41 +00:00
fb5a257735
* fix(library): show album artist in album grids (#1056) Prefer album-artist tags over track artist when building album rows from the local index, and align grid cards with OpenSubsonic displayArtist. * fix(library): align album artist in FTS, search, and offline paths (#1056) Apply album-artist preference in FTS album dedupe and live search, fix offline pin hydration order, and use albumArtistDisplayName in remaining cheap UI/export/download call sites. * docs(changelog): album artist grid fix for compilations (PR #1057) * fix(library): parity guard and live-search album artist helper (#1056) Align SQL ELSE branch with pick_album_group_artist trimming, add parity test, and use albumArtistDisplayName in LiveSearch and MobileSearchOverlay.
88 lines
3.0 KiB
TypeScript
88 lines
3.0 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
albumArtistDisplayName,
|
|
deriveAlbumArtistRefs,
|
|
deriveAlbumHeaderArtistRefs,
|
|
} from './deriveAlbumHeaderArtistRefs';
|
|
import type { SubsonicAlbum } from '../../api/subsonicTypes';
|
|
import { makeSubsonicSong } from '@/test/helpers/factories';
|
|
|
|
const baseAlbum = (): SubsonicAlbum => ({
|
|
id: 'al-1',
|
|
name: 'Test Album',
|
|
artist: 'Joined A / B',
|
|
artistId: 'ar-first',
|
|
songCount: 2,
|
|
duration: 100,
|
|
});
|
|
|
|
describe('deriveAlbumArtistRefs', () => {
|
|
it('prefers the OpenSubsonic `artists` array when present', () => {
|
|
const album: SubsonicAlbum = {
|
|
...baseAlbum(),
|
|
artists: [{ id: 'a1', name: 'One' }, { id: 'a2', name: 'Two' }],
|
|
};
|
|
expect(deriveAlbumArtistRefs(album)).toEqual(album.artists);
|
|
});
|
|
|
|
it('uses legacy artist + artistId when no structured refs', () => {
|
|
expect(deriveAlbumArtistRefs(baseAlbum())).toEqual([{ id: 'ar-first', name: 'Joined A / B' }]);
|
|
});
|
|
|
|
it('omits id when artistId is blank', () => {
|
|
expect(deriveAlbumArtistRefs({ ...baseAlbum(), artistId: ' ', artist: 'Solo' }))
|
|
.toEqual([{ name: 'Solo' }]);
|
|
});
|
|
|
|
it('coerces a single-object OpenSubsonic artists payload', () => {
|
|
const album: SubsonicAlbum = {
|
|
...baseAlbum(),
|
|
artists: { id: 'a1', name: 'Solo' } as unknown as SubsonicAlbum['artists'],
|
|
};
|
|
expect(deriveAlbumArtistRefs(album)).toEqual([{ id: 'a1', name: 'Solo' }]);
|
|
});
|
|
|
|
it('prefers OpenSubsonic displayArtist over legacy artist', () => {
|
|
const album: SubsonicAlbum = {
|
|
...baseAlbum(),
|
|
artist: 'Groove Armada',
|
|
displayArtist: 'Underworld',
|
|
};
|
|
expect(deriveAlbumArtistRefs(album)).toEqual([{ id: 'ar-first', name: 'Underworld' }]);
|
|
expect(albumArtistDisplayName(album)).toBe('Underworld');
|
|
});
|
|
});
|
|
|
|
describe('deriveAlbumHeaderArtistRefs', () => {
|
|
it('prefers the album-level `artists` array when present', () => {
|
|
const album: SubsonicAlbum = {
|
|
...baseAlbum(),
|
|
artists: [{ id: 'a1', name: 'One' }, { id: 'a2', name: 'Two' }],
|
|
};
|
|
expect(deriveAlbumHeaderArtistRefs(album, [])).toEqual(album.artists);
|
|
});
|
|
|
|
it('falls back to the first song with `albumArtists`', () => {
|
|
const album = baseAlbum();
|
|
const songs = [
|
|
makeSubsonicSong({
|
|
albumId: album.id,
|
|
album: album.name,
|
|
albumArtists: [{ id: 'b1', name: 'Beta' }, { name: 'Gamma' }],
|
|
}),
|
|
];
|
|
expect(deriveAlbumHeaderArtistRefs(album, songs)).toEqual(songs[0].albumArtists);
|
|
});
|
|
|
|
it('uses legacy artist + artistId when no structured refs', () => {
|
|
const album = baseAlbum();
|
|
const songs = [makeSubsonicSong({ albumId: album.id, album: album.name })];
|
|
expect(deriveAlbumHeaderArtistRefs(album, songs)).toEqual([{ id: 'ar-first', name: 'Joined A / B' }]);
|
|
});
|
|
|
|
it('omits id when artistId is blank', () => {
|
|
const album: SubsonicAlbum = { ...baseAlbum(), artistId: ' ', artist: 'Solo' };
|
|
expect(deriveAlbumHeaderArtistRefs(album, [])).toEqual([{ name: 'Solo' }]);
|
|
});
|
|
});
|