Files
psysonic/src/utils/serverDisplayName.test.ts
T
cucadmuh f6f76723d8 feat(servers/settings): magic string invites, Navidrome admin share, and add-user validation (#258)
* feat(servers): magic string invites and duplicate-aware labels

Add psysonic1- share payloads (URL, Subsonic credentials, optional server
name). Login and add-server forms decode on input and keep magic field
below standard fields. Navidrome User Management generates strings after
PUT password updates where required. Resolve duplicate display names as
username@host in chrome, settings, and login.

* feat(servers): tighten magic-string import and admin share flow

After a decoded magic string, username is read-only and the password field
shows a fixed-length mask so length is not inferred. Password reveal stays
disabled until saved-server quick connect (login) or reopening the add-
server form. Navidrome admin share UI persists the password via API before
copy and adds a plaintext-handling disclaimer. Locales updated.

* feat(settings): save new Navidrome user and copy magic string

Add a non-admin "Save and get magic string" flow: create the user, assign
libraries when needed, then encode credentials to the clipboard. Reuse
the plaintext-handling disclaimer without the password-update hint meant
for edits. Strings added for all locales.

* fix(settings): tighten Navidrome add-user validation and submit feedback

Require username, display name, and a non-empty password (trimmed) for new
users; gate Save and “save and get magic string” on explicit checks with
toast feedback. Show red borders only after a failed submit, and clear
them when the user fills the fields. When editing, keep an empty password
as “unchanged” and validate identity fields with a dedicated message.
Strings: userMgmtValidationMissingIdentity across locales.
2026-04-22 00:45:28 +02:00

37 lines
1.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type { ServerProfile } from '../store/authStore';
import { serverListDisplayLabel, shortHostFromServerUrl } from './serverDisplayName';
function srv(p: Partial<ServerProfile> & Pick<ServerProfile, 'id'>): ServerProfile {
return {
name: '',
url: 'https://example.com',
username: 'u',
password: 'p',
...p,
};
}
describe('shortHostFromServerUrl', () => {
it('strips https and path', () => {
expect(shortHostFromServerUrl('https://music.one.com/v1')).toBe('music.one.com');
});
it('keeps port', () => {
expect(shortHostFromServerUrl('http://127.0.0.1:4533')).toBe('127.0.0.1:4533');
});
});
describe('serverListDisplayLabel', () => {
it('uses short host when name empty', () => {
const a = srv({ id: '1', url: 'https://a.com', username: 'u', password: 'p', name: '' });
expect(serverListDisplayLabel(a, [a])).toBe('a.com');
});
it('disambiguates duplicate names', () => {
const a = srv({ id: '1', url: 'https://music.one.com', username: 'alice', password: 'p', name: 'Home' });
const b = srv({ id: '2', url: 'https://other.net', username: 'bob', password: 'p', name: 'Home' });
const all = [a, b];
expect(serverListDisplayLabel(a, all)).toBe('alice@music.one.com');
expect(serverListDisplayLabel(b, all)).toBe('bob@other.net');
});
});