diff --git a/src/entities/Font/model/store/appliedFontsStore/appliedFontStore.test.ts b/src/entities/Font/model/store/appliedFontsStore/appliedFontStore.test.ts index f2a116e..e1740f7 100644 --- a/src/entities/Font/model/store/appliedFontsStore/appliedFontStore.test.ts +++ b/src/entities/Font/model/store/appliedFontsStore/appliedFontStore.test.ts @@ -12,9 +12,12 @@ import { AppliedFontsManager } from './appliedFontsStore.svelte'; describe('AppliedFontsManager', () => { let manager: AppliedFontsManager; let mockFontFaceSet: any; + let mockFetch: any; + let failUrls: Set; beforeEach(() => { vi.useFakeTimers(); + failUrls = new Set(); mockFontFaceSet = { add: vi.fn(), @@ -22,11 +25,13 @@ describe('AppliedFontsManager', () => { }; // 1. Properly mock FontFace as a constructor function - const MockFontFace = vi.fn(function(this: any, name: string, url: string) { + // The actual implementation passes buffer (ArrayBuffer) as second arg, not URL string + const MockFontFace = vi.fn(function(this: any, name: string, bufferOrUrl: ArrayBuffer | string) { this.name = name; - this.url = url; + this.bufferOrUrl = bufferOrUrl; this.load = vi.fn().mockImplementation(() => { - if (url.includes('fail')) return Promise.reject(new Error('Load failed')); + // For error tests, we track which URLs should fail via failUrls + // The fetch mock will have already rejected for those URLs return Promise.resolve(this); }); }); @@ -44,18 +49,37 @@ describe('AppliedFontsManager', () => { randomUUID: () => '11111111-1111-1111-1111-111111111111' as any, }); + // 3. Mock fetch to return fake ArrayBuffer data + mockFetch = vi.fn((url: string) => { + if (failUrls.has(url)) { + return Promise.reject(new Error('Network error')); + } + return Promise.resolve({ + ok: true, + status: 200, + arrayBuffer: () => Promise.resolve(new ArrayBuffer(8)), + clone: () => ({ + ok: true, + status: 200, + arrayBuffer: () => Promise.resolve(new ArrayBuffer(8)), + }), + } as Response); + }); + vi.stubGlobal('fetch', mockFetch); + manager = new AppliedFontsManager(); }); afterEach(() => { vi.clearAllTimers(); vi.useRealTimers(); + vi.unstubAllGlobals(); }); it('should batch multiple font requests into a single process', async () => { const configs = [ - { id: 'lato-400', name: 'Lato', url: 'lato.ttf', weight: 400 }, - { id: 'lato-700', name: 'Lato', url: 'lato-bold.ttf', weight: 700 }, + { id: 'lato-400', name: 'Lato', url: 'https://example.com/lato.ttf', weight: 400 }, + { id: 'lato-700', name: 'Lato', url: 'https://example.com/lato-bold.ttf', weight: 700 }, ]; manager.touch(configs); @@ -71,7 +95,10 @@ describe('AppliedFontsManager', () => { // Suppress expected console error for clean test logs const spy = vi.spyOn(console, 'error').mockImplementation(() => {}); - const config = { id: 'broken', name: 'Broken', url: 'fail.ttf', weight: 400 }; + const failUrl = 'https://example.com/fail.ttf'; + failUrls.add(failUrl); + + const config = { id: 'broken', name: 'Broken', url: failUrl, weight: 400 }; manager.touch([config]); await vi.advanceTimersByTimeAsync(50); @@ -81,7 +108,7 @@ describe('AppliedFontsManager', () => { }); it('should purge fonts after TTL expires', async () => { - const config = { id: 'ephemeral', name: 'Temp', url: 'temp.ttf', weight: 400 }; + const config = { id: 'ephemeral', name: 'Temp', url: 'https://example.com/temp.ttf', weight: 400 }; manager.touch([config]); await vi.advanceTimersByTimeAsync(50); @@ -96,7 +123,7 @@ describe('AppliedFontsManager', () => { }); it('should NOT purge fonts that are still being "touched"', async () => { - const config = { id: 'active', name: 'Active', url: 'active.ttf', weight: 400 }; + const config = { id: 'active', name: 'Active', url: 'https://example.com/active.ttf', weight: 400 }; manager.touch([config]); await vi.advanceTimersByTimeAsync(50);