Files
psysonic/src/utils/album/deriveAlbumHeaderArtistRefs.test.ts
T
Frank Stellmacher 6e0f076f43 fix(album-card): per-artist click on multi-artist albums (#767)
* refactor(album): rename SubsonicAlbum.albumArtists to artists per OpenSubsonic spec

The OpenSubsonic AlbumID3 object exposes structured album-artist credits as
`artists` (not `albumArtists`); psysonic's internal type used the wrong name,
so `album.albumArtists` was always undefined on the Subsonic responses
Navidrome returns. `deriveAlbumHeaderArtistRefs` therefore never hit its
album-level branch — only the song-level fallback was carrying the multi-
artist split on the album-detail header.

- Rename the field on `SubsonicAlbum` and add the spec-defined `displayArtist`
  string alongside it.
- Extract `deriveAlbumArtistRefs(album)` for the common case where only the
  album object is available (cards, rails); `deriveAlbumHeaderArtistRefs`
  now uses it after the song-level fallback. The song-level `albumArtists`
  field is unchanged (the spec does name it that way on child songs).
- Tests cover both helpers and the legacy `artist` + `artistId` fallback.

* fix(album-card): per-artist click on multi-artist albums

The artist subtitle under an album card rendered the full `album.artist`
string ("Melvins • Napalm Death") as a single link that always navigated to
`album.artistId`. On the artist-detail page that id is the page's own
artist — so the click resolved to the URL the user was already on and the
router silently no-op'd, with no visible effect.

Render through the existing `OpenArtistRefInline` component instead, fed
from the structured `artists` array via `deriveAlbumArtistRefs`. Each
artist becomes its own ·-separated link; the single-artist fallback is the
same legacy `artist` + `artistId` pair as before, so the behaviour on
servers that don't expose the structured field is unchanged.

* docs(changelog): note PR #767 under Fixed in v1.46.0
2026-05-18 02:03:01 +02:00

66 lines
2.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { 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' }]);
});
});
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' }]);
});
});