Initial commit: MetonaEditor v0.1.0

This commit is contained in:
2026-07-23 16:23:07 +08:00
commit 22e867eda8
32 changed files with 17621 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
/**
* 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 始终返回 0CSS 动画由浏览器管理)', () => {
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');
});
});