/** * animations.js 单元测试 * 覆盖 animationUtils 全部 API + createAnimation 工厂 */ import { animationUtils, createAnimation, animationPresets } from '../src/animations.js'; import { ANIMATIONS } from '../src/constants.js'; describe('animationUtils - 默认动画注册', () => { test('默认动画已注册到 animationMap', () => { const names = animationUtils.getAnimationNames(); // 与 constants.js 中 ANIMATIONS 的键一致 Object.keys(ANIMATIONS).forEach((name) => { expect(names).toContain(name); }); }); test('内置动画类型完整', () => { const names = animationUtils.getAnimationNames(); expect(names).toEqual(expect.arrayContaining([ 'slide', 'fade', 'scale', 'bounce', 'flip', 'rotate', 'zoom', ])); }); }); describe('animationUtils - register / unregister / get', () => { test('register 注册自定义动画', () => { animationUtils.register('custom', { enter: { opacity: 0 }, leave: { opacity: 0 }, duration: 500, easing: 'ease-in', }); const a = animationUtils.get('custom'); expect(a).not.toBeNull(); expect(a.name).toBe('custom'); expect(a.duration).toBe(500); expect(a.easing).toBe('ease-in'); expect(a.enter).toEqual({ opacity: 0 }); }); test('register 缺省字段使用默认值', () => { animationUtils.register('minimal', {}); const a = animationUtils.get('minimal'); expect(a.enter).toEqual({}); expect(a.leave).toEqual({}); expect(a.duration).toBe(300); expect(a.easing).toBe('cubic-bezier(0.4, 0, 0.2, 1)'); }); test('unregister 移除动画', () => { animationUtils.register('tmp', { duration: 100 }); expect(animationUtils.get('tmp')).not.toBeNull(); animationUtils.unregister('tmp'); // 移除后回退到 ANIMATIONS,但 'tmp' 不在 ANIMATIONS 中,返回 null expect(animationUtils.get('tmp')).toBeNull(); }); test('get 不存在的动画返回 null', () => { expect(animationUtils.get('non-existent-animation')).toBeNull(); }); test('get 内置动画返回配置', () => { const a = animationUtils.get('slide'); expect(a).not.toBeNull(); // 内置动画直接存储 ANIMATIONS.slide 配置(不含 name 字段,只有 register() 才添加) expect(a.duration).toBe(ANIMATIONS.slide.duration); expect(a.enter).toEqual(ANIMATIONS.slide.enter); }); }); describe('animationUtils - 查询 API', () => { test('getAnimationNames 返回数组', () => { const names = animationUtils.getAnimationNames(); expect(Array.isArray(names)).toBe(true); expect(names.length).toBeGreaterThan(0); }); test('getActiveCount 始终返回 0(CSS 动画由浏览器管理)', () => { expect(animationUtils.getActiveCount()).toBe(0); }); test('cancelAll 不抛错(无操作)', () => { expect(() => animationUtils.cancelAll()).not.toThrow(); }); }); describe('animationUtils - reset / destroy', () => { test('reset 清除自定义动画并恢复内置', () => { animationUtils.register('tmp1', {}); animationUtils.register('tmp2', {}); expect(animationUtils.getAnimationNames()).toContain('tmp1'); animationUtils.reset(); const names = animationUtils.getAnimationNames(); expect(names).not.toContain('tmp1'); expect(names).not.toContain('tmp2'); // 内置动画仍存在 expect(names).toContain('slide'); expect(names).toContain('fade'); }); test('destroy 清空所有动画', () => { animationUtils.destroy(); expect(animationUtils.getAnimationNames()).toEqual([]); // destroy 后 reset 可恢复 animationUtils.reset(); expect(animationUtils.getAnimationNames().length).toBeGreaterThan(0); }); }); describe('createAnimation 工厂', () => { test('返回完整动画对象', () => { const a = createAnimation({ enter: { transform: 'translateX(10px)' }, leave: { transform: 'translateX(-10px)' }, duration: 600, easing: 'linear', }); expect(a.enter).toEqual({ transform: 'translateX(10px)' }); expect(a.leave).toEqual({ transform: 'translateX(-10px)' }); expect(a.duration).toBe(600); expect(a.easing).toBe('linear'); }); test('缺省字段使用默认值', () => { const a = createAnimation({}); expect(a.enter).toEqual({}); expect(a.leave).toEqual({}); expect(a.duration).toBe(300); expect(a.easing).toBe('cubic-bezier(0.4, 0, 0.2, 1)'); }); test('无参数也能创建', () => { const a = createAnimation(); expect(a).toBeDefined(); expect(a.duration).toBe(300); }); }); describe('animationPresets', () => { test('导出为对象', () => { expect(typeof animationPresets).toBe('object'); }); });