725 lines
25 KiB
TypeScript
725 lines
25 KiB
TypeScript
/**
|
|
* @vitest-environment jsdom
|
|
*/
|
|
import {
|
|
DEFAULT_FONT_SIZE,
|
|
DEFAULT_FONT_WEIGHT,
|
|
DEFAULT_LETTER_SPACING,
|
|
DEFAULT_LINE_HEIGHT,
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
} from '$entities/Font';
|
|
import {
|
|
beforeEach,
|
|
describe,
|
|
expect,
|
|
it,
|
|
vi,
|
|
} from 'vitest';
|
|
import {
|
|
type TypographySettings,
|
|
TypographySettingsManager,
|
|
} from './settingsManager.svelte';
|
|
|
|
/**
|
|
* Test Strategy for TypographySettingsManager
|
|
*
|
|
* This test suite validates the TypographySettingsManager state management logic.
|
|
* These are unit tests for the manager logic, separate from component rendering.
|
|
*
|
|
* NOTE: Svelte 5's $effect runs in microtasks, so we need to flush effects
|
|
* after state changes to test reactive behavior. This is a limitation of unit
|
|
* testing Svelte 5 reactive code in Node.js.
|
|
*
|
|
* Test Coverage:
|
|
* 1. Initialization: Loading from storage, creating controls with correct values
|
|
* 2. Multiplier System: Changing multiplier updates font size display
|
|
* 3. Base Size Proxy: UI changes update #baseSize via the proxy effect
|
|
* 4. Storage Sync: Changes to controls sync to storage (via $effect)
|
|
* 5. Reset Functionality: Clearing storage resets all controls
|
|
* 6. Rendered Size: base * multiplier calculation
|
|
* 7. Control Getters: Return correct control instances
|
|
*/
|
|
|
|
// Helper to flush Svelte effects (they run in microtasks)
|
|
async function flushEffects() {
|
|
await Promise.resolve();
|
|
await Promise.resolve();
|
|
}
|
|
|
|
describe('TypographySettingsManager - Unit Tests', () => {
|
|
let mockStorage: TypographySettings;
|
|
let mockPersistentStore: {
|
|
value: TypographySettings;
|
|
clear: () => void;
|
|
};
|
|
|
|
const createMockPersistentStore = (initialValue: TypographySettings) => {
|
|
let value = initialValue;
|
|
return {
|
|
get value() {
|
|
return value;
|
|
},
|
|
set value(v: TypographySettings) {
|
|
value = v;
|
|
},
|
|
clear() {
|
|
value = {
|
|
fontSize: DEFAULT_FONT_SIZE,
|
|
fontWeight: DEFAULT_FONT_WEIGHT,
|
|
lineHeight: DEFAULT_LINE_HEIGHT,
|
|
letterSpacing: DEFAULT_LETTER_SPACING,
|
|
};
|
|
},
|
|
};
|
|
};
|
|
|
|
beforeEach(() => {
|
|
// Reset mock storage with default values before each test
|
|
mockStorage = {
|
|
fontSize: DEFAULT_FONT_SIZE,
|
|
fontWeight: DEFAULT_FONT_WEIGHT,
|
|
lineHeight: DEFAULT_LINE_HEIGHT,
|
|
letterSpacing: DEFAULT_LETTER_SPACING,
|
|
};
|
|
mockPersistentStore = createMockPersistentStore(mockStorage);
|
|
});
|
|
|
|
describe('Initialization', () => {
|
|
it('creates manager with default values from storage', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
expect(manager.baseSize).toBe(DEFAULT_FONT_SIZE);
|
|
expect(manager.weight).toBe(DEFAULT_FONT_WEIGHT);
|
|
expect(manager.height).toBe(DEFAULT_LINE_HEIGHT);
|
|
expect(manager.spacing).toBe(DEFAULT_LETTER_SPACING);
|
|
});
|
|
|
|
it('creates manager with saved values from storage', () => {
|
|
mockStorage = {
|
|
fontSize: 72,
|
|
fontWeight: 700,
|
|
lineHeight: 1.8,
|
|
letterSpacing: 0.05,
|
|
};
|
|
mockPersistentStore = createMockPersistentStore(mockStorage);
|
|
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
expect(manager.baseSize).toBe(72);
|
|
expect(manager.weight).toBe(700);
|
|
expect(manager.height).toBe(1.8);
|
|
expect(manager.spacing).toBe(0.05);
|
|
});
|
|
|
|
it('initializes font size control with base size multiplied by current multiplier (1)', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
expect(manager.sizeControl?.value).toBe(DEFAULT_FONT_SIZE);
|
|
});
|
|
|
|
it('returns all controls via controls getter', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
const controls = manager.controls;
|
|
expect(controls).toHaveLength(4);
|
|
expect(controls.map(c => c.id)).toEqual([
|
|
'font_size',
|
|
'font_weight',
|
|
'line_height',
|
|
'letter_spacing',
|
|
]);
|
|
});
|
|
|
|
it('returns individual controls via specific getters', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
expect(manager.sizeControl).toBeDefined();
|
|
expect(manager.weightControl).toBeDefined();
|
|
expect(manager.heightControl).toBeDefined();
|
|
expect(manager.spacingControl).toBeDefined();
|
|
|
|
// Control instances have value, min, max, step, isAtMax, isAtMin, increase, decrease
|
|
expect(manager.sizeControl).toHaveProperty('value');
|
|
expect(manager.weightControl).toHaveProperty('value');
|
|
expect(manager.heightControl).toHaveProperty('value');
|
|
expect(manager.spacingControl).toHaveProperty('value');
|
|
});
|
|
|
|
it('control instances have expected interface', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
const ctrl = manager.sizeControl!;
|
|
expect(typeof ctrl.value).toBe('number');
|
|
expect(typeof ctrl.min).toBe('number');
|
|
expect(typeof ctrl.max).toBe('number');
|
|
expect(typeof ctrl.step).toBe('number');
|
|
expect(typeof ctrl.isAtMax).toBe('boolean');
|
|
expect(typeof ctrl.isAtMin).toBe('boolean');
|
|
expect(typeof ctrl.increase).toBe('function');
|
|
expect(typeof ctrl.decrease).toBe('function');
|
|
});
|
|
});
|
|
|
|
describe('Multiplier System', () => {
|
|
it('has default multiplier of 1', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
expect(manager.multiplier).toBe(1);
|
|
});
|
|
|
|
it('updates multiplier when set', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.multiplier = 0.75;
|
|
expect(manager.multiplier).toBe(0.75);
|
|
|
|
manager.multiplier = 0.5;
|
|
expect(manager.multiplier).toBe(0.5);
|
|
});
|
|
|
|
it('does not update multiplier if set to same value', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
const originalSizeValue = manager.sizeControl?.value;
|
|
|
|
manager.multiplier = 1; // Same as default
|
|
|
|
expect(manager.sizeControl?.value).toBe(originalSizeValue);
|
|
});
|
|
|
|
it('updates font size control display value when multiplier changes', () => {
|
|
mockStorage = { fontSize: 48, fontWeight: 400, lineHeight: 1.5, letterSpacing: 0 };
|
|
mockPersistentStore = createMockPersistentStore(mockStorage);
|
|
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
// Initial state: base = 48, multiplier = 1, display = 48
|
|
expect(manager.baseSize).toBe(48);
|
|
expect(manager.sizeControl?.value).toBe(48);
|
|
|
|
// Change multiplier to 0.75
|
|
manager.multiplier = 0.75;
|
|
// Display should be 48 * 0.75 = 36
|
|
expect(manager.sizeControl?.value).toBe(36);
|
|
|
|
// Change multiplier to 0.5
|
|
manager.multiplier = 0.5;
|
|
// Display should be 48 * 0.5 = 24
|
|
expect(manager.sizeControl?.value).toBe(24);
|
|
|
|
// Base size should remain unchanged
|
|
expect(manager.baseSize).toBe(48);
|
|
});
|
|
|
|
it('updates font size control display value when multiplier increases', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
// Start with multiplier 0.5
|
|
manager.multiplier = 0.5;
|
|
expect(manager.sizeControl?.value).toBe(DEFAULT_FONT_SIZE * 0.5);
|
|
|
|
// Increase to 0.75
|
|
manager.multiplier = 0.75;
|
|
expect(manager.sizeControl?.value).toBe(DEFAULT_FONT_SIZE * 0.75);
|
|
|
|
// Increase to 1.0
|
|
manager.multiplier = 1;
|
|
expect(manager.sizeControl?.value).toBe(DEFAULT_FONT_SIZE);
|
|
});
|
|
});
|
|
|
|
describe('Base Size Setter', () => {
|
|
it('updates baseSize when set directly', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.baseSize = 72;
|
|
|
|
expect(manager.baseSize).toBe(72);
|
|
});
|
|
|
|
it('updates size control value when baseSize is set', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.baseSize = 60;
|
|
|
|
expect(manager.sizeControl?.value).toBe(60);
|
|
});
|
|
|
|
it('applies multiplier to size control when baseSize is set', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.multiplier = 0.5;
|
|
manager.baseSize = 60;
|
|
|
|
expect(manager.sizeControl?.value).toBe(30); // 60 * 0.5
|
|
});
|
|
});
|
|
|
|
describe('Rendered Size Calculation', () => {
|
|
it('calculates renderedSize as baseSize * multiplier', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
expect(manager.renderedSize).toBe(DEFAULT_FONT_SIZE * 1);
|
|
});
|
|
|
|
it('updates renderedSize when multiplier changes', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.multiplier = 0.5;
|
|
expect(manager.renderedSize).toBe(DEFAULT_FONT_SIZE * 0.5);
|
|
|
|
manager.multiplier = 0.75;
|
|
expect(manager.renderedSize).toBe(DEFAULT_FONT_SIZE * 0.75);
|
|
});
|
|
|
|
it('updates renderedSize when baseSize changes', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.baseSize = 72;
|
|
expect(manager.renderedSize).toBe(72);
|
|
|
|
manager.multiplier = 0.5;
|
|
expect(manager.renderedSize).toBe(36);
|
|
});
|
|
});
|
|
|
|
describe('Base Size Proxy Effect (UI -> baseSize)', () => {
|
|
// NOTE: The proxy effect that updates baseSize when the control value changes
|
|
// runs in a $effect, which is asynchronous in unit tests. We test the
|
|
// synchronous behavior here (baseSize setter) and note that the full
|
|
// proxy effect behavior should be tested in E2E tests.
|
|
|
|
it('does NOT immediately update baseSize from control change (effect is async)', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
const originalBaseSize = manager.baseSize;
|
|
|
|
// Change the control value directly
|
|
manager.sizeControl!.value = 60;
|
|
|
|
// baseSize is NOT updated immediately because the effect runs in microtasks
|
|
expect(manager.baseSize).toBe(originalBaseSize);
|
|
});
|
|
|
|
it('updates baseSize via direct setter (synchronous)', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.baseSize = 60;
|
|
|
|
expect(manager.baseSize).toBe(60);
|
|
expect(manager.sizeControl?.value).toBe(60);
|
|
});
|
|
});
|
|
|
|
describe('Storage Sync (Controls -> Storage)', () => {
|
|
// NOTE: Storage sync happens via $effect which runs in microtasks.
|
|
// In unit tests, we verify the initial sync and test async behavior.
|
|
|
|
it('has initial values in storage from constructor', () => {
|
|
mockStorage = {
|
|
fontSize: 60,
|
|
fontWeight: 500,
|
|
lineHeight: 1.6,
|
|
letterSpacing: 0.02,
|
|
};
|
|
mockPersistentStore = createMockPersistentStore(mockStorage);
|
|
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
// Initial values are loaded from storage
|
|
expect(manager.baseSize).toBe(60);
|
|
expect(manager.weight).toBe(500);
|
|
expect(manager.height).toBe(1.6);
|
|
expect(manager.spacing).toBe(0.02);
|
|
});
|
|
|
|
it('syncs to storage after effect flush (async)', async () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.baseSize = 72;
|
|
|
|
// Storage is NOT updated immediately
|
|
expect(mockPersistentStore.value.fontSize).toBe(DEFAULT_FONT_SIZE);
|
|
|
|
// After flushing effects, storage should be updated
|
|
await flushEffects();
|
|
expect(mockPersistentStore.value.fontSize).toBe(72);
|
|
});
|
|
|
|
it('syncs control changes to storage after effect flush (async)', async () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.weightControl!.value = 700;
|
|
|
|
// After flushing effects
|
|
await flushEffects();
|
|
expect(mockPersistentStore.value.fontWeight).toBe(700);
|
|
});
|
|
|
|
it('syncs height control changes to storage after effect flush (async)', async () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.heightControl!.value = 1.8;
|
|
|
|
await flushEffects();
|
|
expect(mockPersistentStore.value.lineHeight).toBe(1.8);
|
|
});
|
|
|
|
it('syncs spacing control changes to storage after effect flush (async)', async () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.spacingControl!.value = 0.05;
|
|
|
|
await flushEffects();
|
|
expect(mockPersistentStore.value.letterSpacing).toBe(0.05);
|
|
});
|
|
});
|
|
|
|
describe('Control Value Getters', () => {
|
|
it('returns current weight value', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
expect(manager.weight).toBe(DEFAULT_FONT_WEIGHT);
|
|
|
|
manager.weightControl!.value = 700;
|
|
expect(manager.weight).toBe(700);
|
|
});
|
|
|
|
it('returns current height value', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
expect(manager.height).toBe(DEFAULT_LINE_HEIGHT);
|
|
|
|
manager.heightControl!.value = 1.8;
|
|
expect(manager.height).toBe(1.8);
|
|
});
|
|
|
|
it('returns current spacing value', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
expect(manager.spacing).toBe(DEFAULT_LETTER_SPACING);
|
|
|
|
manager.spacingControl!.value = 0.05;
|
|
expect(manager.spacing).toBe(0.05);
|
|
});
|
|
|
|
it('returns default value when control is not found', () => {
|
|
// Create a manager with empty configs (no controls)
|
|
const manager = new TypographySettingsManager([], mockPersistentStore);
|
|
|
|
expect(manager.weight).toBe(DEFAULT_FONT_WEIGHT);
|
|
expect(manager.height).toBe(DEFAULT_LINE_HEIGHT);
|
|
expect(manager.spacing).toBe(DEFAULT_LETTER_SPACING);
|
|
});
|
|
});
|
|
|
|
describe('Reset Functionality', () => {
|
|
it('resets all controls to default values', () => {
|
|
mockStorage = {
|
|
fontSize: 72,
|
|
fontWeight: 700,
|
|
lineHeight: 1.8,
|
|
letterSpacing: 0.05,
|
|
};
|
|
mockPersistentStore = createMockPersistentStore(mockStorage);
|
|
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
// Modify values
|
|
manager.baseSize = 80;
|
|
manager.weightControl!.value = 900;
|
|
manager.heightControl!.value = 2.0;
|
|
manager.spacingControl!.value = 0.1;
|
|
|
|
// Reset
|
|
manager.reset();
|
|
|
|
// Check all values are reset to defaults
|
|
expect(manager.baseSize).toBe(DEFAULT_FONT_SIZE);
|
|
expect(manager.weight).toBe(DEFAULT_FONT_WEIGHT);
|
|
expect(manager.height).toBe(DEFAULT_LINE_HEIGHT);
|
|
expect(manager.spacing).toBe(DEFAULT_LETTER_SPACING);
|
|
});
|
|
|
|
it('calls storage.clear() on reset', () => {
|
|
const clearSpy = vi.fn();
|
|
mockPersistentStore = {
|
|
get value() {
|
|
return mockStorage;
|
|
},
|
|
set value(v: TypographySettings) {
|
|
mockStorage = v;
|
|
},
|
|
clear: clearSpy,
|
|
};
|
|
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.reset();
|
|
|
|
expect(clearSpy).toHaveBeenCalled();
|
|
});
|
|
|
|
it('respects multiplier when resetting font size control', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.multiplier = 0.5;
|
|
manager.baseSize = 80;
|
|
|
|
manager.reset();
|
|
|
|
// Font size control should show default * multiplier
|
|
expect(manager.sizeControl?.value).toBe(DEFAULT_FONT_SIZE * 0.5);
|
|
expect(manager.baseSize).toBe(DEFAULT_FONT_SIZE);
|
|
});
|
|
});
|
|
|
|
describe('Complex Scenarios', () => {
|
|
it('handles changing multiplier then modifying baseSize', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
// Change multiplier
|
|
manager.multiplier = 0.5;
|
|
expect(manager.sizeControl?.value).toBe(DEFAULT_FONT_SIZE * 0.5);
|
|
|
|
// Change baseSize
|
|
manager.baseSize = 60;
|
|
expect(manager.sizeControl?.value).toBe(30); // 60 * 0.5
|
|
expect(manager.baseSize).toBe(60);
|
|
|
|
// Change multiplier again
|
|
manager.multiplier = 1;
|
|
expect(manager.sizeControl?.value).toBe(60); // 60 * 1
|
|
expect(manager.baseSize).toBe(60);
|
|
});
|
|
|
|
it('maintains correct renderedSize throughout changes', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
// Initial: 48 * 1 = 48
|
|
expect(manager.renderedSize).toBe(48);
|
|
|
|
// Change baseSize: 60 * 1 = 60
|
|
manager.baseSize = 60;
|
|
expect(manager.renderedSize).toBe(60);
|
|
|
|
// Change multiplier: 60 * 0.5 = 30
|
|
manager.multiplier = 0.5;
|
|
expect(manager.renderedSize).toBe(30);
|
|
|
|
// Change baseSize again: 72 * 0.5 = 36
|
|
manager.baseSize = 72;
|
|
expect(manager.renderedSize).toBe(36);
|
|
});
|
|
|
|
it('handles multiple control changes in sequence', async () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
// Change multiple controls
|
|
manager.baseSize = 72;
|
|
manager.weightControl!.value = 700;
|
|
manager.heightControl!.value = 1.8;
|
|
manager.spacingControl!.value = 0.05;
|
|
|
|
// After flushing effects, verify all are synced to storage
|
|
await flushEffects();
|
|
expect(mockPersistentStore.value.fontSize).toBe(72);
|
|
expect(mockPersistentStore.value.fontWeight).toBe(700);
|
|
expect(mockPersistentStore.value.lineHeight).toBe(1.8);
|
|
expect(mockPersistentStore.value.letterSpacing).toBe(0.05);
|
|
});
|
|
});
|
|
|
|
describe('Edge Cases', () => {
|
|
it('handles multiplier of 1 (no change)', () => {
|
|
mockStorage = { fontSize: 48, fontWeight: 400, lineHeight: 1.5, letterSpacing: 0 };
|
|
mockPersistentStore = createMockPersistentStore(mockStorage);
|
|
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.multiplier = 1;
|
|
|
|
expect(manager.sizeControl?.value).toBe(48);
|
|
expect(manager.baseSize).toBe(48);
|
|
});
|
|
|
|
it('handles very small multiplier', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.baseSize = 100;
|
|
manager.multiplier = 0.1;
|
|
|
|
expect(manager.sizeControl?.value).toBe(10);
|
|
expect(manager.renderedSize).toBe(10);
|
|
});
|
|
|
|
it('handles large base size with multiplier', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.baseSize = 100;
|
|
manager.multiplier = 0.75;
|
|
|
|
expect(manager.sizeControl?.value).toBe(75);
|
|
expect(manager.renderedSize).toBe(75);
|
|
});
|
|
|
|
it('handles floating point precision in multiplier', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
manager.baseSize = 48;
|
|
manager.multiplier = 0.5;
|
|
|
|
// 48 * 0.5 = 24 (exact, no rounding needed)
|
|
expect(manager.sizeControl?.value).toBe(24);
|
|
expect(manager.renderedSize).toBe(24);
|
|
|
|
// 48 * 0.33 = 15.84 -> rounds to 16 (step precision is 1)
|
|
manager.multiplier = 0.33;
|
|
expect(manager.sizeControl?.value).toBe(16);
|
|
expect(manager.renderedSize).toBeCloseTo(15.84);
|
|
});
|
|
|
|
it('handles control methods (increase/decrease)', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
const initialWeight = manager.weight;
|
|
manager.weightControl!.increase();
|
|
expect(manager.weight).toBe(initialWeight + 100);
|
|
|
|
manager.weightControl!.decrease();
|
|
expect(manager.weight).toBe(initialWeight);
|
|
});
|
|
|
|
it('handles control boundary conditions', () => {
|
|
const manager = new TypographySettingsManager(
|
|
DEFAULT_TYPOGRAPHY_CONTROLS_DATA,
|
|
mockPersistentStore,
|
|
);
|
|
|
|
const sizeCtrl = manager.sizeControl!;
|
|
|
|
// Test min boundary
|
|
sizeCtrl.value = 5;
|
|
expect(sizeCtrl.value).toBe(sizeCtrl.min); // Should clamp to MIN_FONT_SIZE (8)
|
|
|
|
// Test max boundary
|
|
sizeCtrl.value = 200;
|
|
expect(sizeCtrl.value).toBe(sizeCtrl.max); // Should clamp to MAX_FONT_SIZE (100)
|
|
});
|
|
});
|
|
});
|