feat(ComparisonStore): pin fontA/fontB to prevent eviction while on-screen
This commit is contained in:
@@ -134,6 +134,19 @@ export class ComparisonStore {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Effect 4: Pin fontA/fontB so eviction never removes on-screen fonts
|
||||||
|
$effect(() => {
|
||||||
|
const fa = this.#fontA;
|
||||||
|
const fb = this.#fontB;
|
||||||
|
const w = typographySettingsStore.weight;
|
||||||
|
if (fa) appliedFontsManager.pin(fa.id, w, fa.features?.isVariable);
|
||||||
|
if (fb) appliedFontsManager.pin(fb.id, w, fb.features?.isVariable);
|
||||||
|
return () => {
|
||||||
|
if (fa) appliedFontsManager.unpin(fa.id, w, fa.features?.isVariable);
|
||||||
|
if (fb) appliedFontsManager.unpin(fb.id, w, fb.features?.isVariable);
|
||||||
|
};
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,15 +53,19 @@ vi.mock('$shared/lib/helpers/createPersistentStore/createPersistentStore.svelte'
|
|||||||
|
|
||||||
// ── $entities/Font mock — keep real BatchFontStore, stub singletons ───────────
|
// ── $entities/Font mock — keep real BatchFontStore, stub singletons ───────────
|
||||||
|
|
||||||
vi.mock('$entities/Font', async () => {
|
vi.mock('$entities/Font', async importOriginal => {
|
||||||
|
const actual = await importOriginal<typeof import('$entities/Font')>();
|
||||||
const { BatchFontStore } = await import(
|
const { BatchFontStore } = await import(
|
||||||
'$entities/Font/model/store/batchFontStore.svelte'
|
'$entities/Font/model/store/batchFontStore.svelte'
|
||||||
);
|
);
|
||||||
return {
|
return {
|
||||||
|
...actual,
|
||||||
BatchFontStore,
|
BatchFontStore,
|
||||||
fontStore: { fonts: [] },
|
fontStore: { fonts: [] },
|
||||||
appliedFontsManager: {
|
appliedFontsManager: {
|
||||||
touch: vi.fn(),
|
touch: vi.fn(),
|
||||||
|
pin: vi.fn(),
|
||||||
|
unpin: vi.fn(),
|
||||||
getFontStatus: vi.fn(),
|
getFontStatus: vi.fn(),
|
||||||
ready: vi.fn(() => Promise.resolve()),
|
ready: vi.fn(() => Promise.resolve()),
|
||||||
},
|
},
|
||||||
@@ -80,9 +84,20 @@ vi.mock('$features/SetupFont', () => ({
|
|||||||
})),
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
vi.mock('$features/SetupFont/model', () => ({
|
||||||
|
typographySettingsStore: {
|
||||||
|
weight: 400,
|
||||||
|
renderedSize: 48,
|
||||||
|
reset: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
// ── Imports (after mocks) ─────────────────────────────────────────────────────
|
// ── Imports (after mocks) ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
import { fontStore } from '$entities/Font';
|
import {
|
||||||
|
appliedFontsManager,
|
||||||
|
fontStore,
|
||||||
|
} from '$entities/Font';
|
||||||
import * as proxyFonts from '$entities/Font/api/proxy/proxyFonts';
|
import * as proxyFonts from '$entities/Font/api/proxy/proxyFonts';
|
||||||
import { ComparisonStore } from './comparisonStore.svelte';
|
import { ComparisonStore } from './comparisonStore.svelte';
|
||||||
|
|
||||||
@@ -209,4 +224,55 @@ describe('ComparisonStore', () => {
|
|||||||
expect(store.fontB).toBeUndefined();
|
expect(store.fontB).toBeUndefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Pin / Unpin ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('Pin / Unpin (eviction guard)', () => {
|
||||||
|
it('pins fontA and fontB when they are loaded', async () => {
|
||||||
|
mockStorage._value.fontAId = mockFontA.id;
|
||||||
|
mockStorage._value.fontBId = mockFontB.id;
|
||||||
|
vi.spyOn(proxyFonts, 'fetchFontsByIds').mockResolvedValue([mockFontA, mockFontB]);
|
||||||
|
|
||||||
|
new ComparisonStore();
|
||||||
|
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(appliedFontsManager.pin).toHaveBeenCalledWith(
|
||||||
|
mockFontA.id,
|
||||||
|
400,
|
||||||
|
mockFontA.features?.isVariable,
|
||||||
|
);
|
||||||
|
expect(appliedFontsManager.pin).toHaveBeenCalledWith(
|
||||||
|
mockFontB.id,
|
||||||
|
400,
|
||||||
|
mockFontB.features?.isVariable,
|
||||||
|
);
|
||||||
|
}, { timeout: 2000 });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unpins the old font when fontA is replaced', async () => {
|
||||||
|
mockStorage._value.fontAId = mockFontA.id;
|
||||||
|
mockStorage._value.fontBId = mockFontB.id;
|
||||||
|
vi.spyOn(proxyFonts, 'fetchFontsByIds').mockResolvedValue([mockFontA, mockFontB]);
|
||||||
|
|
||||||
|
const store = new ComparisonStore();
|
||||||
|
await vi.waitFor(() => expect(store.fontA?.id).toBe(mockFontA.id), { timeout: 2000 });
|
||||||
|
|
||||||
|
const mockFontC: typeof mockFontA = { ...mockFontA, id: 'playfair', name: 'Playfair Display' };
|
||||||
|
vi.spyOn(proxyFonts, 'fetchFontsByIds').mockResolvedValue([mockFontC, mockFontB]);
|
||||||
|
store.fontA = mockFontC;
|
||||||
|
|
||||||
|
await vi.waitFor(() => {
|
||||||
|
expect(appliedFontsManager.unpin).toHaveBeenCalledWith(
|
||||||
|
mockFontA.id,
|
||||||
|
400,
|
||||||
|
mockFontA.features?.isVariable,
|
||||||
|
);
|
||||||
|
expect(appliedFontsManager.pin).toHaveBeenCalledWith(
|
||||||
|
mockFontC.id,
|
||||||
|
400,
|
||||||
|
mockFontC.features?.isVariable,
|
||||||
|
);
|
||||||
|
}, { timeout: 2000 });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user