@sc4rfurryx/proteusjs
Version:
The Modern Web Development Framework for Accessible, Responsive, and High-Performance Applications. Intelligent container queries, fluid typography, WCAG compliance, and performance optimization.
437 lines (352 loc) • 13.8 kB
text/typescript
/**
* LineHeightOptimization Test Suite
* Comprehensive tests for line height optimization system
*/
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { LineHeightOptimization, LineHeightConfig } from '../LineHeightOptimization';
// Mock ResizeObserver
global.ResizeObserver = vi.fn().mockImplementation((callback) => ({
observe: vi.fn(),
unobserve: vi.fn(),
disconnect: vi.fn(),
}));
describe('LineHeightOptimization', () => {
let lineHeightOptimization: LineHeightOptimization;
let testElement: HTMLElement;
beforeEach(() => {
// Clean up DOM
document.body.innerHTML = '';
// Create test element
testElement = document.createElement('p');
testElement.textContent = 'This is a test paragraph with some content to test line height optimization.';
testElement.style.fontSize = '16px';
document.body.appendChild(testElement);
lineHeightOptimization = new LineHeightOptimization();
// Mock getComputedStyle
vi.spyOn(window, 'getComputedStyle').mockReturnValue({
fontSize: '16px',
fontFamily: 'Arial, sans-serif'
} as CSSStyleDeclaration);
// Mock getBoundingClientRect
vi.spyOn(testElement, 'getBoundingClientRect').mockReturnValue({
width: 400,
height: 100,
top: 0,
left: 0,
bottom: 100,
right: 400,
x: 0,
y: 0,
toJSON: () => ({})
} as DOMRect);
});
afterEach(() => {
lineHeightOptimization.destroy();
document.body.innerHTML = '';
vi.restoreAllMocks();
});
describe('Basic Line Height Optimization', () => {
it('should optimize line height with default settings', () => {
const result = lineHeightOptimization.optimizeLineHeight(testElement);
expect(result.lineHeight).toBeGreaterThan(1);
expect(result.lineHeightCSS).toBeTruthy();
expect(result.reasoning).toBeInstanceOf(Array);
expect(result.reasoning.length).toBeGreaterThan(0);
expect(testElement.style.lineHeight).toBe(result.lineHeightCSS);
});
it('should apply different density settings', () => {
const compactResult = lineHeightOptimization.optimizeLineHeight(testElement, {
density: 'compact'
});
const spaciousResult = lineHeightOptimization.optimizeLineHeight(testElement, {
density: 'spacious'
});
expect(compactResult.lineHeight).toBeLessThan(spaciousResult.lineHeight);
});
it('should add debugging attributes', () => {
const result = lineHeightOptimization.optimizeLineHeight(testElement);
expect(testElement.getAttribute('data-proteus-line-height')).toBe(
result.lineHeight.toString()
);
expect(testElement.getAttribute('data-proteus-line-height-reasoning')).toBeTruthy();
});
});
describe('Content Type Adjustments', () => {
it('should adjust for heading content', () => {
const headingResult = lineHeightOptimization.optimizeLineHeight(testElement, {
contentType: 'heading'
});
const bodyResult = lineHeightOptimization.optimizeLineHeight(testElement, {
contentType: 'body'
});
expect(headingResult.lineHeight).toBeLessThan(bodyResult.lineHeight);
expect(headingResult.adjustments).toContain(
expect.stringContaining('Content type (heading)')
);
});
it('should adjust for code content', () => {
const codeResult = lineHeightOptimization.optimizeLineHeight(testElement, {
contentType: 'code'
});
const bodyResult = lineHeightOptimization.optimizeLineHeight(testElement, {
contentType: 'body'
});
expect(codeResult.lineHeight).toBeGreaterThan(bodyResult.lineHeight);
expect(codeResult.adjustments).toContain(
expect.stringContaining('Content type (code)')
);
});
it('should adjust for caption content', () => {
const captionResult = lineHeightOptimization.optimizeLineHeight(testElement, {
contentType: 'caption'
});
const bodyResult = lineHeightOptimization.optimizeLineHeight(testElement, {
contentType: 'body'
});
expect(captionResult.lineHeight).toBeLessThan(bodyResult.lineHeight);
});
});
describe('Language-Specific Adjustments', () => {
it('should adjust for Chinese language', () => {
const chineseResult = lineHeightOptimization.optimizeLineHeight(testElement, {
language: 'zh-CN'
});
const englishResult = lineHeightOptimization.optimizeLineHeight(testElement, {
language: 'en'
});
expect(chineseResult.lineHeight).toBeGreaterThan(englishResult.lineHeight);
expect(chineseResult.adjustments).toContain(
expect.stringContaining('Language (zh)')
);
});
it('should adjust for Japanese language', () => {
const japaneseResult = lineHeightOptimization.optimizeLineHeight(testElement, {
language: 'ja'
});
const englishResult = lineHeightOptimization.optimizeLineHeight(testElement, {
language: 'en'
});
expect(japaneseResult.lineHeight).toBeGreaterThan(englishResult.lineHeight);
});
it('should handle unknown languages gracefully', () => {
const unknownResult = lineHeightOptimization.optimizeLineHeight(testElement, {
language: 'unknown-lang'
});
expect(unknownResult.lineHeight).toBeGreaterThan(0);
expect(unknownResult.adjustments).not.toContain(
expect.stringContaining('Language (unknown-lang)')
);
});
});
describe('Line Length Adjustments', () => {
it('should adjust for very short lines', () => {
// Mock narrow container
vi.spyOn(testElement, 'getBoundingClientRect').mockReturnValue({
width: 100, // Very narrow
height: 100,
top: 0,
left: 0,
bottom: 100,
right: 100,
x: 0,
y: 0,
toJSON: () => ({})
} as DOMRect);
const result = lineHeightOptimization.optimizeLineHeight(testElement);
expect(result.adjustments).toContain(
expect.stringContaining('Line length')
);
});
it('should adjust for very long lines', () => {
// Mock very wide container
vi.spyOn(testElement, 'getBoundingClientRect').mockReturnValue({
width: 1200, // Very wide
height: 100,
top: 0,
left: 0,
bottom: 100,
right: 1200,
x: 0,
y: 0,
toJSON: () => ({})
} as DOMRect);
const result = lineHeightOptimization.optimizeLineHeight(testElement);
expect(result.adjustments).toContain(
expect.stringContaining('Line length')
);
});
});
describe('Font Size Adjustments', () => {
it('should adjust for small font sizes', () => {
vi.spyOn(window, 'getComputedStyle').mockReturnValue({
fontSize: '10px', // Very small
fontFamily: 'Arial, sans-serif'
} as CSSStyleDeclaration);
const result = lineHeightOptimization.optimizeLineHeight(testElement);
expect(result.adjustments).toContain(
expect.stringContaining('Font size (10px)')
);
});
it('should adjust for large font sizes', () => {
vi.spyOn(window, 'getComputedStyle').mockReturnValue({
fontSize: '32px', // Very large
fontFamily: 'Arial, sans-serif'
} as CSSStyleDeclaration);
const result = lineHeightOptimization.optimizeLineHeight(testElement);
expect(result.adjustments).toContain(
expect.stringContaining('Font size (32px)')
);
});
});
describe('Accessibility Compliance', () => {
it('should enforce WCAG AA compliance', () => {
const config: LineHeightConfig = {
density: 'compact', // Would normally result in low line height
accessibility: 'AA',
enforceAccessibility: true
};
const result = lineHeightOptimization.optimizeLineHeight(testElement, config);
expect(result.lineHeight).toBeGreaterThanOrEqual(1.5); // WCAG AA minimum
expect(result.accessibilityCompliant).toBe(true);
});
it('should enforce WCAG AAA compliance', () => {
const config: LineHeightConfig = {
density: 'compact',
accessibility: 'AAA',
enforceAccessibility: true
};
const result = lineHeightOptimization.optimizeLineHeight(testElement, config);
expect(result.lineHeight).toBeGreaterThanOrEqual(1.6); // WCAG AAA minimum
expect(result.accessibilityCompliant).toBe(true);
});
it('should allow bypassing accessibility constraints', () => {
const config: LineHeightConfig = {
density: 'compact',
accessibility: 'AA',
enforceAccessibility: false
};
const result = lineHeightOptimization.optimizeLineHeight(testElement, config);
// Should not enforce minimum if disabled
expect(result.lineHeight).toBeLessThan(1.5);
});
it('should mark non-compliant results', () => {
const config: LineHeightConfig = {
density: 'compact',
accessibility: 'AAA',
enforceAccessibility: true
};
const result = lineHeightOptimization.optimizeLineHeight(testElement, config);
if (result.lineHeight < 1.6) {
expect(result.accessibilityCompliant).toBe(false);
} else {
expect(result.accessibilityCompliant).toBe(true);
}
});
});
describe('User Preferences', () => {
it('should respect user line spacing preferences', () => {
// Mock CSS custom property for user preferences
vi.spyOn(window, 'getComputedStyle').mockImplementation((element) => {
if (element === document.documentElement) {
return {
getPropertyValue: (prop: string) => {
if (prop === '--user-line-spacing') return '1.2';
return '';
}
} as CSSStyleDeclaration;
}
return {
fontSize: '16px',
fontFamily: 'Arial, sans-serif'
} as CSSStyleDeclaration;
});
const result = lineHeightOptimization.optimizeLineHeight(testElement, {
respectUserPreferences: true
});
expect(result.adjustments).toContain(
expect.stringContaining('User preferences')
);
});
it('should handle missing user preferences gracefully', () => {
const result = lineHeightOptimization.optimizeLineHeight(testElement, {
respectUserPreferences: true
});
expect(result.lineHeight).toBeGreaterThan(0);
});
});
describe('Element Management', () => {
it('should update line height when element changes', () => {
const config: LineHeightConfig = {
density: 'comfortable'
};
lineHeightOptimization.optimizeLineHeight(testElement, config);
const initialLineHeight = testElement.style.lineHeight;
// Change element size
vi.spyOn(testElement, 'getBoundingClientRect').mockReturnValue({
width: 800, // Much wider
height: 100,
top: 0,
left: 0,
bottom: 100,
right: 800,
x: 0,
y: 0,
toJSON: () => ({})
} as DOMRect);
lineHeightOptimization.updateLineHeight(testElement);
expect(testElement.style.lineHeight).not.toBe(initialLineHeight);
});
it('should remove optimization', () => {
lineHeightOptimization.optimizeLineHeight(testElement);
expect(testElement.style.lineHeight).toBeTruthy();
expect(testElement.getAttribute('data-proteus-line-height')).toBeTruthy();
lineHeightOptimization.removeOptimization(testElement);
expect(testElement.style.lineHeight).toBeFalsy();
expect(testElement.getAttribute('data-proteus-line-height')).toBeNull();
});
it('should handle elements without optimization', () => {
expect(() => {
lineHeightOptimization.removeOptimization(testElement);
}).not.toThrow();
expect(() => {
lineHeightOptimization.updateLineHeight(testElement);
}).not.toThrow();
});
});
describe('Error Handling', () => {
it('should handle errors gracefully', () => {
const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
// Force an error by providing invalid config
const config = null as any;
const result = lineHeightOptimization.optimizeLineHeight(testElement, config);
expect(result.lineHeight).toBeGreaterThan(0);
expect(result.reasoning).toContain('Error occurred, using default');
expect(consoleSpy).toHaveBeenCalled();
consoleSpy.mockRestore();
});
it('should clean up resources on destroy', () => {
lineHeightOptimization.optimizeLineHeight(testElement);
expect(() => {
lineHeightOptimization.destroy();
}).not.toThrow();
});
});
describe('Precision and Rounding', () => {
it('should round line height to reasonable precision', () => {
const result = lineHeightOptimization.optimizeLineHeight(testElement);
// Should be rounded to 3 decimal places
const decimalPlaces = (result.lineHeight.toString().split('.')[1] || '').length;
expect(decimalPlaces).toBeLessThanOrEqual(3);
});
it('should provide detailed reasoning', () => {
const result = lineHeightOptimization.optimizeLineHeight(testElement, {
density: 'comfortable',
contentType: 'body',
language: 'en',
accessibility: 'AA'
});
expect(result.reasoning.length).toBeGreaterThan(1);
expect(result.reasoning[0]).toContain('comfortable density');
});
});
});