- web/: React + Vite + TypeScript 前端 - backend/: Django + DRF + SimpleJWT 后端 - prototype/: HTML 设计原型 - docs/: PRD 和设计评审文档 - test: 单元测试 + E2E 极限测试
272 lines
10 KiB
TypeScript
272 lines
10 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { useInputBarStore } from '../../src/store/inputBar';
|
|
|
|
function createMockFile(name: string, type: string): File {
|
|
return new File(['mock'], name, { type });
|
|
}
|
|
|
|
describe('InputBar Store', () => {
|
|
beforeEach(() => {
|
|
useInputBarStore.getState().reset();
|
|
});
|
|
|
|
describe('Initial State', () => {
|
|
it('should have correct default values', () => {
|
|
const state = useInputBarStore.getState();
|
|
expect(state.generationType).toBe('video');
|
|
expect(state.mode).toBe('universal');
|
|
expect(state.model).toBe('seedance_2.0');
|
|
expect(state.aspectRatio).toBe('21:9');
|
|
expect(state.duration).toBe(15);
|
|
expect(state.prompt).toBe('');
|
|
expect(state.references).toEqual([]);
|
|
expect(state.firstFrame).toBeNull();
|
|
expect(state.lastFrame).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('Generation Type', () => {
|
|
it('should set generation type', () => {
|
|
useInputBarStore.getState().setGenerationType('image');
|
|
expect(useInputBarStore.getState().generationType).toBe('image');
|
|
});
|
|
});
|
|
|
|
describe('Model Selection', () => {
|
|
it('should set model', () => {
|
|
useInputBarStore.getState().setModel('seedance_2.0_fast');
|
|
expect(useInputBarStore.getState().model).toBe('seedance_2.0_fast');
|
|
});
|
|
});
|
|
|
|
describe('Aspect Ratio', () => {
|
|
it('should set aspect ratio and save as previous', () => {
|
|
useInputBarStore.getState().setAspectRatio('16:9');
|
|
const state = useInputBarStore.getState();
|
|
expect(state.aspectRatio).toBe('16:9');
|
|
expect(state.prevAspectRatio).toBe('16:9');
|
|
});
|
|
});
|
|
|
|
describe('Duration', () => {
|
|
it('should set duration in universal mode and save as previous', () => {
|
|
useInputBarStore.getState().setDuration(10);
|
|
const state = useInputBarStore.getState();
|
|
expect(state.duration).toBe(10);
|
|
expect(state.prevDuration).toBe(10);
|
|
});
|
|
|
|
it('should set duration in keyframe mode without saving as previous', () => {
|
|
useInputBarStore.getState().switchMode('keyframe');
|
|
useInputBarStore.getState().setDuration(10);
|
|
const state = useInputBarStore.getState();
|
|
expect(state.duration).toBe(10);
|
|
expect(state.prevDuration).toBe(15); // original default
|
|
});
|
|
});
|
|
|
|
describe('Prompt', () => {
|
|
it('should set prompt text', () => {
|
|
useInputBarStore.getState().setPrompt('test prompt');
|
|
expect(useInputBarStore.getState().prompt).toBe('test prompt');
|
|
});
|
|
});
|
|
|
|
describe('Universal References', () => {
|
|
it('should add references', () => {
|
|
const files = [createMockFile('img1.jpg', 'image/jpeg')];
|
|
useInputBarStore.getState().addReferences(files);
|
|
expect(useInputBarStore.getState().references).toHaveLength(1);
|
|
expect(useInputBarStore.getState().references[0].type).toBe('image');
|
|
});
|
|
|
|
it('should limit references to 5', () => {
|
|
const files = Array.from({ length: 6 }, (_, i) =>
|
|
createMockFile(`img${i}.jpg`, 'image/jpeg')
|
|
);
|
|
useInputBarStore.getState().addReferences(files);
|
|
expect(useInputBarStore.getState().references).toHaveLength(5);
|
|
});
|
|
|
|
it('should not add more when already at 5', () => {
|
|
const files = Array.from({ length: 5 }, (_, i) =>
|
|
createMockFile(`img${i}.jpg`, 'image/jpeg')
|
|
);
|
|
useInputBarStore.getState().addReferences(files);
|
|
expect(useInputBarStore.getState().references).toHaveLength(5);
|
|
|
|
useInputBarStore.getState().addReferences([createMockFile('extra.jpg', 'image/jpeg')]);
|
|
expect(useInputBarStore.getState().references).toHaveLength(5);
|
|
});
|
|
|
|
it('should detect video files correctly', () => {
|
|
const files = [createMockFile('vid.mp4', 'video/mp4')];
|
|
useInputBarStore.getState().addReferences(files);
|
|
expect(useInputBarStore.getState().references[0].type).toBe('video');
|
|
});
|
|
|
|
it('should remove a reference by id', () => {
|
|
const files = [createMockFile('img1.jpg', 'image/jpeg')];
|
|
useInputBarStore.getState().addReferences(files);
|
|
const id = useInputBarStore.getState().references[0].id;
|
|
|
|
useInputBarStore.getState().removeReference(id);
|
|
expect(useInputBarStore.getState().references).toHaveLength(0);
|
|
});
|
|
|
|
it('should call revokeObjectURL when removing reference', () => {
|
|
const files = [createMockFile('img1.jpg', 'image/jpeg')];
|
|
useInputBarStore.getState().addReferences(files);
|
|
const id = useInputBarStore.getState().references[0].id;
|
|
|
|
useInputBarStore.getState().removeReference(id);
|
|
expect(URL.revokeObjectURL).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should clear all references', () => {
|
|
const files = [
|
|
createMockFile('img1.jpg', 'image/jpeg'),
|
|
createMockFile('img2.jpg', 'image/jpeg'),
|
|
];
|
|
useInputBarStore.getState().addReferences(files);
|
|
useInputBarStore.getState().clearReferences();
|
|
expect(useInputBarStore.getState().references).toHaveLength(0);
|
|
});
|
|
});
|
|
|
|
describe('Keyframe Frames', () => {
|
|
it('should set first frame', () => {
|
|
const file = createMockFile('first.jpg', 'image/jpeg');
|
|
useInputBarStore.getState().setFirstFrame(file);
|
|
const state = useInputBarStore.getState();
|
|
expect(state.firstFrame).not.toBeNull();
|
|
expect(state.firstFrame!.label).toBe('首帧');
|
|
});
|
|
|
|
it('should set last frame', () => {
|
|
const file = createMockFile('last.jpg', 'image/jpeg');
|
|
useInputBarStore.getState().setLastFrame(file);
|
|
const state = useInputBarStore.getState();
|
|
expect(state.lastFrame).not.toBeNull();
|
|
expect(state.lastFrame!.label).toBe('尾帧');
|
|
});
|
|
|
|
it('should clear first frame when setting null', () => {
|
|
useInputBarStore.getState().setFirstFrame(createMockFile('first.jpg', 'image/jpeg'));
|
|
useInputBarStore.getState().setFirstFrame(null);
|
|
expect(useInputBarStore.getState().firstFrame).toBeNull();
|
|
});
|
|
|
|
it('should revoke old URL when replacing frame', () => {
|
|
useInputBarStore.getState().setFirstFrame(createMockFile('a.jpg', 'image/jpeg'));
|
|
useInputBarStore.getState().setFirstFrame(createMockFile('b.jpg', 'image/jpeg'));
|
|
expect(URL.revokeObjectURL).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('canSubmit', () => {
|
|
it('should return false when no content', () => {
|
|
expect(useInputBarStore.getState().canSubmit()).toBe(false);
|
|
});
|
|
|
|
it('should return true when prompt has text', () => {
|
|
useInputBarStore.getState().setPrompt('hello');
|
|
expect(useInputBarStore.getState().canSubmit()).toBe(true);
|
|
});
|
|
|
|
it('should return false for whitespace-only prompt', () => {
|
|
useInputBarStore.getState().setPrompt(' ');
|
|
expect(useInputBarStore.getState().canSubmit()).toBe(false);
|
|
});
|
|
|
|
it('should return true when universal references exist', () => {
|
|
useInputBarStore.getState().addReferences([createMockFile('img.jpg', 'image/jpeg')]);
|
|
expect(useInputBarStore.getState().canSubmit()).toBe(true);
|
|
});
|
|
|
|
it('should return true when first frame exists in keyframe mode', () => {
|
|
useInputBarStore.getState().switchMode('keyframe');
|
|
useInputBarStore.getState().setFirstFrame(createMockFile('first.jpg', 'image/jpeg'));
|
|
expect(useInputBarStore.getState().canSubmit()).toBe(true);
|
|
});
|
|
|
|
it('should return true when last frame exists in keyframe mode', () => {
|
|
useInputBarStore.getState().switchMode('keyframe');
|
|
useInputBarStore.getState().setLastFrame(createMockFile('last.jpg', 'image/jpeg'));
|
|
expect(useInputBarStore.getState().canSubmit()).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Mode Switching', () => {
|
|
it('should switch to keyframe mode with correct defaults', () => {
|
|
useInputBarStore.getState().switchMode('keyframe');
|
|
const state = useInputBarStore.getState();
|
|
expect(state.mode).toBe('keyframe');
|
|
expect(state.duration).toBe(5);
|
|
expect(state.references).toEqual([]);
|
|
});
|
|
|
|
it('should clear universal references when switching to keyframe', () => {
|
|
useInputBarStore.getState().addReferences([createMockFile('img.jpg', 'image/jpeg')]);
|
|
useInputBarStore.getState().switchMode('keyframe');
|
|
expect(useInputBarStore.getState().references).toEqual([]);
|
|
});
|
|
|
|
it('should restore aspect ratio and duration when switching back to universal', () => {
|
|
useInputBarStore.getState().setAspectRatio('16:9');
|
|
useInputBarStore.getState().setDuration(10);
|
|
useInputBarStore.getState().switchMode('keyframe');
|
|
useInputBarStore.getState().switchMode('universal');
|
|
|
|
const state = useInputBarStore.getState();
|
|
expect(state.aspectRatio).toBe('16:9');
|
|
expect(state.duration).toBe(10);
|
|
});
|
|
|
|
it('should clear keyframe data when switching back to universal', () => {
|
|
useInputBarStore.getState().switchMode('keyframe');
|
|
useInputBarStore.getState().setFirstFrame(createMockFile('first.jpg', 'image/jpeg'));
|
|
useInputBarStore.getState().setLastFrame(createMockFile('last.jpg', 'image/jpeg'));
|
|
useInputBarStore.getState().switchMode('universal');
|
|
|
|
const state = useInputBarStore.getState();
|
|
expect(state.firstFrame).toBeNull();
|
|
expect(state.lastFrame).toBeNull();
|
|
});
|
|
|
|
it('should not do anything when switching to same mode', () => {
|
|
useInputBarStore.getState().setPrompt('test');
|
|
useInputBarStore.getState().switchMode('universal');
|
|
expect(useInputBarStore.getState().prompt).toBe('test');
|
|
});
|
|
|
|
it('should preserve prompt text across mode switches', () => {
|
|
useInputBarStore.getState().setPrompt('my prompt');
|
|
useInputBarStore.getState().switchMode('keyframe');
|
|
expect(useInputBarStore.getState().prompt).toBe('my prompt');
|
|
useInputBarStore.getState().switchMode('universal');
|
|
expect(useInputBarStore.getState().prompt).toBe('my prompt');
|
|
});
|
|
});
|
|
|
|
describe('Reset', () => {
|
|
it('should reset all state to defaults', () => {
|
|
useInputBarStore.getState().setPrompt('hello');
|
|
useInputBarStore.getState().setModel('seedance_2.0_fast');
|
|
useInputBarStore.getState().setAspectRatio('16:9');
|
|
useInputBarStore.getState().setDuration(5);
|
|
useInputBarStore.getState().addReferences([createMockFile('img.jpg', 'image/jpeg')]);
|
|
|
|
useInputBarStore.getState().reset();
|
|
const state = useInputBarStore.getState();
|
|
expect(state.prompt).toBe('');
|
|
expect(state.model).toBe('seedance_2.0');
|
|
expect(state.aspectRatio).toBe('21:9');
|
|
expect(state.duration).toBe(15);
|
|
expect(state.references).toEqual([]);
|
|
expect(state.mode).toBe('universal');
|
|
expect(state.generationType).toBe('video');
|
|
});
|
|
});
|
|
});
|