feat(web/m7-363): branding helper + window.__MINSTREL__ consumer
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { appName, description, pageTitle } from './branding';
|
||||||
|
|
||||||
|
const setGlobal = (g: { appName: string; description: string } | undefined) => {
|
||||||
|
if (g === undefined) {
|
||||||
|
delete (window as { __MINSTREL__?: unknown }).__MINSTREL__;
|
||||||
|
} else {
|
||||||
|
(window as { __MINSTREL__?: typeof g }).__MINSTREL__ = g;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('branding helpers', () => {
|
||||||
|
beforeEach(() => setGlobal(undefined));
|
||||||
|
afterEach(() => setGlobal(undefined));
|
||||||
|
|
||||||
|
it('appName returns the global value when set', () => {
|
||||||
|
setGlobal({ appName: 'Family Jukebox', description: 'home' });
|
||||||
|
expect(appName()).toBe('Family Jukebox');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('appName falls back to "Minstrel" when global is undefined', () => {
|
||||||
|
expect(appName()).toBe('Minstrel');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('appName falls back to "Minstrel" when value contains an unrendered Go token', () => {
|
||||||
|
setGlobal({ appName: '{{ .AppName }}', description: 'x' });
|
||||||
|
expect(appName()).toBe('Minstrel');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('description returns the global value when set', () => {
|
||||||
|
setGlobal({ appName: 'Minstrel', description: 'Our home library.' });
|
||||||
|
expect(description()).toBe('Our home library.');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('description falls back to a default tagline when unset', () => {
|
||||||
|
expect(description()).toContain('Self-hosted music server');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('pageTitle with section returns "{appName} · {section}"', () => {
|
||||||
|
setGlobal({ appName: 'Minstrel', description: 'x' });
|
||||||
|
expect(pageTitle('Search')).toBe('Minstrel · Search');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('pageTitle without section returns just the appName', () => {
|
||||||
|
setGlobal({ appName: 'Minstrel', description: 'x' });
|
||||||
|
expect(pageTitle()).toBe('Minstrel');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('pageTitle composes nested sections via the · separator', () => {
|
||||||
|
setGlobal({ appName: 'Minstrel', description: 'x' });
|
||||||
|
expect(pageTitle('Library · Artists')).toBe('Minstrel · Library · Artists');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
__MINSTREL__?: { appName: string; description: string };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DEFAULT_APP_NAME = 'Minstrel';
|
||||||
|
const DEFAULT_DESCRIPTION =
|
||||||
|
'Self-hosted music server with Subsonic compatibility, smart shuffle, and Lidarr integration.';
|
||||||
|
|
||||||
|
const isRenderedToken = (v: string | undefined): v is string =>
|
||||||
|
typeof v === 'string' && v.length > 0 && !v.includes('{{');
|
||||||
|
|
||||||
|
export const appName = (): string => {
|
||||||
|
if (typeof window === 'undefined') return DEFAULT_APP_NAME;
|
||||||
|
return isRenderedToken(window.__MINSTREL__?.appName)
|
||||||
|
? window.__MINSTREL__!.appName
|
||||||
|
: DEFAULT_APP_NAME;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const description = (): string => {
|
||||||
|
if (typeof window === 'undefined') return DEFAULT_DESCRIPTION;
|
||||||
|
return isRenderedToken(window.__MINSTREL__?.description)
|
||||||
|
? window.__MINSTREL__!.description
|
||||||
|
: DEFAULT_DESCRIPTION;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const pageTitle = (section?: string): string =>
|
||||||
|
section ? `${appName()} · ${section}` : appName();
|
||||||
|
|
||||||
|
// Ensure this file is treated as a module (the global declaration above
|
||||||
|
// otherwise turns it into a global script).
|
||||||
|
export {};
|
||||||
Reference in New Issue
Block a user