feat(getFontUrl): create a helper function to choose font url
This commit is contained in:
592
src/entities/Font/lib/getFontUrl/getFontUrl.test.ts
Normal file
592
src/entities/Font/lib/getFontUrl/getFontUrl.test.ts
Normal file
@@ -0,0 +1,592 @@
|
|||||||
|
import {
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
it,
|
||||||
|
} from 'vitest';
|
||||||
|
import type { UnifiedFont } from '../../model/types';
|
||||||
|
import { getFontUrl } from './getFontUrl';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to create a minimal UnifiedFont mock for testing
|
||||||
|
*/
|
||||||
|
function createMockFont(
|
||||||
|
overrides: Partial<UnifiedFont> = {},
|
||||||
|
): UnifiedFont {
|
||||||
|
const baseFont: UnifiedFont = {
|
||||||
|
id: 'test-font',
|
||||||
|
name: 'Test Font',
|
||||||
|
provider: 'google',
|
||||||
|
category: 'sans-serif',
|
||||||
|
subsets: ['latin'],
|
||||||
|
variants: [],
|
||||||
|
styles: {},
|
||||||
|
metadata: {
|
||||||
|
cachedAt: Date.now(),
|
||||||
|
},
|
||||||
|
features: {
|
||||||
|
isVariable: false,
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...baseFont, ...overrides };
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('getFontUrl', () => {
|
||||||
|
describe('basic logic', () => {
|
||||||
|
it('returns URL for exact weight match in variants', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
'700': 'https://example.com/font-700.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 400);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-400.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns URL for weight 700', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'700': 'https://example.com/font-700.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-700.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns URL for weight 100 (lightest)', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'100': 'https://example.com/font-100.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 100);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-100.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns URL for weight 900 (boldest)', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'900': 'https://example.com/font-900.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 900);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-900.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns URL for variable font (backend maps weight to VF URL)', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-variable.woff2',
|
||||||
|
'700': 'https://example.com/font-variable.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result400 = getFontUrl(font, 400);
|
||||||
|
const result700 = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
expect(result400).toBe('https://example.com/font-variable.woff2');
|
||||||
|
expect(result700).toBe('https://example.com/font-variable.woff2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fallback logic', () => {
|
||||||
|
it('falls back to regular when exact weight not found', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
regular: 'https://example.com/font-regular.woff2',
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-regular.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to variant 400 when exact weight and regular not found', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-400.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('falls back to variant regular when exact weight, regular, and 400 not found', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'700': 'https://example.com/font-700.woff2',
|
||||||
|
'regular': 'https://example.com/font-regular.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 400);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-regular.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prefers regular over variants.400 for fallback', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
regular: 'https://example.com/font-regular.woff2',
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-regular.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined when no fallback options available', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'700': 'https://example.com/font-700.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 400);
|
||||||
|
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined for font with empty styles', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 400);
|
||||||
|
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for font with undefined styles (invalid font data)', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: undefined as any,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => getFontUrl(font, 400)).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('edge cases', () => {
|
||||||
|
it('handles font with only regular URL (legacy format)', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
regular: 'https://example.com/font-regular.woff2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-regular.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles font with only variants object', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
'700': 'https://example.com/font-700.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result400 = getFontUrl(font, 400);
|
||||||
|
const result700 = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
expect(result400).toBe('https://example.com/font-400.woff2');
|
||||||
|
expect(result700).toBe('https://example.com/font-700.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles font with variants but no requested weight', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-400.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles Google Fonts style with legacy URLs', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
regular: 'https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKOzY.woff2',
|
||||||
|
bold: 'https://fonts.gstatic.com/s/roboto/v30/KFOlCnqEu92Fr1Mu72xWUlvAx05IsDqlA.woff2',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
expect(result).toBe('https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKOzY.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles Fontshare fonts with multiple weights', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'100': 'https://cdn.fontshare.com/wf/font-100.woff2',
|
||||||
|
'200': 'https://cdn.fontshare.com/wf/font-200.woff2',
|
||||||
|
'300': 'https://cdn.fontshare.com/wf/font-300.woff2',
|
||||||
|
'400': 'https://cdn.fontshare.com/wf/font-400.woff2',
|
||||||
|
'500': 'https://cdn.fontshare.com/wf/font-500.woff2',
|
||||||
|
'600': 'https://cdn.fontshare.com/wf/font-600.woff2',
|
||||||
|
'700': 'https://cdn.fontshare.com/wf/font-700.woff2',
|
||||||
|
'800': 'https://cdn.fontshare.com/wf/font-800.woff2',
|
||||||
|
'900': 'https://cdn.fontshare.com/wf/font-900.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test all valid weights
|
||||||
|
for (const weight of [100, 200, 300, 400, 500, 600, 700, 800, 900]) {
|
||||||
|
const result = getFontUrl(font, weight);
|
||||||
|
expect(result).toBe(`https://cdn.fontshare.com/wf/font-${weight}.woff2`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles font with partial weight coverage', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-regular.woff2',
|
||||||
|
'700': 'https://example.com/font-bold.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result400 = getFontUrl(font, 400);
|
||||||
|
const result700 = getFontUrl(font, 700);
|
||||||
|
const result500 = getFontUrl(font, 500);
|
||||||
|
|
||||||
|
expect(result400).toBe('https://example.com/font-regular.woff2');
|
||||||
|
expect(result700).toBe('https://example.com/font-bold.woff2');
|
||||||
|
expect(result500).toBe('https://example.com/font-regular.woff2'); // Fallback
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles font with variants.regular as fallback', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'700': 'https://example.com/font-bold.woff2',
|
||||||
|
'regular': 'https://example.com/font-regular.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 400);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-regular.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles empty variants object', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 400);
|
||||||
|
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns undefined when variant URL is null and no fallback available', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': null as any,
|
||||||
|
'700': 'https://example.com/font-bold.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 400);
|
||||||
|
|
||||||
|
// null is falsy, so it falls back to regular, 400, and then regular variant
|
||||||
|
// All are undefined, so returns undefined
|
||||||
|
expect(result).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('boundary tests', () => {
|
||||||
|
it('handles lowest valid weight (100)', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'100': 'https://example.com/font-100.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 100);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-100.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles highest valid weight (900)', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'900': 'https://example.com/font-900.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 900);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-900.woff2');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles middle weight (500)', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'500': 'https://example.com/font-500.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, 500);
|
||||||
|
|
||||||
|
expect(result).toBe('https://example.com/font-500.woff2');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('invalid weights', () => {
|
||||||
|
it('throws error for weight below 100', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => getFontUrl(font, 99)).toThrow('Invalid weight: 99');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for weight above 900', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => getFontUrl(font, 901)).toThrow('Invalid weight: 901');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for weight 0', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => getFontUrl(font, 0)).toThrow('Invalid weight: 0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for negative weight', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => getFontUrl(font, -100)).toThrow('Invalid weight: -100');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for non-numeric weight', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// @ts-ignore - Testing invalid input type
|
||||||
|
expect(() => getFontUrl(font, '400' as any)).toThrow('Invalid weight: 400');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for decimal weight', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => getFontUrl(font, 450.5)).toThrow('Invalid weight: 450.5');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for weight with step of 50 (not supported)', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => getFontUrl(font, 450)).toThrow('Invalid weight: 450');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for weight with step of 10 (not supported)', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => getFontUrl(font, 410)).toThrow('Invalid weight: 410');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for NaN weight', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => getFontUrl(font, NaN)).toThrow('Invalid weight: NaN');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws error for Infinity weight', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => getFontUrl(font, Infinity)).toThrow('Invalid weight: Infinity');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws descriptive error message', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://example.com/font-400.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
getFontUrl(font, 999);
|
||||||
|
expect.fail('Expected function to throw');
|
||||||
|
} catch (error) {
|
||||||
|
expect(error).toBeInstanceOf(Error);
|
||||||
|
expect((error as Error).message).toBe('Invalid weight: 999');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('provider-specific tests', () => {
|
||||||
|
it('handles Google Fonts with variable fonts', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
provider: 'google',
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKOzY.woff2',
|
||||||
|
'700': 'https://fonts.gstatic.com/s/roboto/v30/KFOmCnqEu92Fr1Mu72xKOzY.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result400 = getFontUrl(font, 400);
|
||||||
|
const result700 = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
// Variable fonts return the same URL for all weights
|
||||||
|
expect(result400).toBe(result700);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles Fontshare fonts with static weights', () => {
|
||||||
|
const font = createMockFont({
|
||||||
|
provider: 'fontshare',
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
'400': 'https://cdn.fontshare.com/wf/satoshi-regular.woff2',
|
||||||
|
'700': 'https://cdn.fontshare.com/wf/satoshi-bold.woff2',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result400 = getFontUrl(font, 400);
|
||||||
|
const result700 = getFontUrl(font, 700);
|
||||||
|
|
||||||
|
expect(result400).toBe('https://cdn.fontshare.com/wf/satoshi-regular.woff2');
|
||||||
|
expect(result700).toBe('https://cdn.fontshare.com/wf/satoshi-bold.woff2');
|
||||||
|
expect(result400).not.toBe(result700);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('all valid weights test', () => {
|
||||||
|
it('handles all valid weight values', () => {
|
||||||
|
const validWeights = [100, 200, 300, 400, 500, 600, 700, 800, 900];
|
||||||
|
|
||||||
|
validWeights.forEach(weight => {
|
||||||
|
const font = createMockFont({
|
||||||
|
styles: {
|
||||||
|
variants: {
|
||||||
|
[weight.toString()]: `https://example.com/font-${weight}.woff2`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = getFontUrl(font, weight);
|
||||||
|
expect(result).toBe(`https://example.com/font-${weight}.woff2`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
29
src/entities/Font/lib/getFontUrl/getFontUrl.ts
Normal file
29
src/entities/Font/lib/getFontUrl/getFontUrl.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import type {
|
||||||
|
FontWeight,
|
||||||
|
UnifiedFont,
|
||||||
|
} from '../../model';
|
||||||
|
|
||||||
|
const SIZES = [100, 200, 300, 400, 500, 600, 700, 800, 900];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a URL for a font based on the provided font and weight.
|
||||||
|
* @param font - The font object.
|
||||||
|
* @param weight - The weight of the font.
|
||||||
|
* @returns The URL for the font.
|
||||||
|
*/
|
||||||
|
export function getFontUrl(font: UnifiedFont, weight: number): string | undefined {
|
||||||
|
if (!SIZES.includes(weight)) {
|
||||||
|
throw new Error(`Invalid weight: ${weight}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const weightKey = weight.toString() as FontWeight;
|
||||||
|
|
||||||
|
// 1. Try exact match (Backend now maps "100".."900" to VF URL if variable)
|
||||||
|
if (font.styles.variants?.[weightKey]) {
|
||||||
|
return font.styles.variants[weightKey];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Fallbacks for Static Fonts (if exact weight missing)
|
||||||
|
// Try 'regular' or '400' as safe defaults
|
||||||
|
return font.styles.regular || font.styles.variants?.['400'] || font.styles.variants?.['regular'];
|
||||||
|
}
|
||||||
@@ -4,3 +4,5 @@ export {
|
|||||||
normalizeGoogleFont,
|
normalizeGoogleFont,
|
||||||
normalizeGoogleFonts,
|
normalizeGoogleFonts,
|
||||||
} from './normalize/normalize';
|
} from './normalize/normalize';
|
||||||
|
|
||||||
|
export { getFontUrl } from './getFontUrl/getFontUrl';
|
||||||
|
|||||||
Reference in New Issue
Block a user