video-shuoshan/web/test/unit/bugfixVerification.test.ts
zyc ffe92f7b15 Initial commit: 即梦视频生成平台
- web/: React + Vite + TypeScript 前端
- backend/: Django + DRF + SimpleJWT 后端
- prototype/: HTML 设计原型
- docs/: PRD 和设计评审文档
- test: 单元测试 + E2E 极限测试
2026-03-13 09:59:33 +08:00

168 lines
6.4 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest';
import { readFileSync } from 'fs';
import { resolve } from 'path';
import { useInputBarStore } from '../../src/store/inputBar';
function createMockFile(name: string, type: string, sizeInBytes?: number): File {
const content = sizeInBytes ? new Uint8Array(sizeInBytes) : new Uint8Array([0]);
return new File([content], name, { type });
}
describe('Bug Fix Verification — Previous Bugs', () => {
describe('BUG-001: GenerationCard model display (was hardcoded)', () => {
it('should use task.model dynamically in GenerationCard source', () => {
const src = readFileSync(
resolve(__dirname, '../../src/components/GenerationCard.tsx'),
'utf-8'
);
// Should NOT contain a bare hardcoded "Seedance 2.0" in the meta section
// Instead should reference task.model
expect(src).toContain('task.model');
// The ternary pattern confirms dynamic rendering
expect(src).toMatch(/task\.model\s*===\s*'seedance_2\.0'/);
});
});
describe('BUG-002: File size validation', () => {
it('UniversalUpload should enforce image <20MB and video <100MB limits', () => {
const src = readFileSync(
resolve(__dirname, '../../src/components/UniversalUpload.tsx'),
'utf-8'
);
expect(src).toContain('20 * 1024 * 1024');
expect(src).toContain('100 * 1024 * 1024');
expect(src).toContain('图片文件不能超过20MB');
expect(src).toContain('视频文件不能超过100MB');
});
it('InputBar drag-drop should enforce file size limits', () => {
const src = readFileSync(
resolve(__dirname, '../../src/components/InputBar.tsx'),
'utf-8'
);
expect(src).toContain('20 * 1024 * 1024');
expect(src).toContain('100 * 1024 * 1024');
});
it('KeyframeUpload should enforce image <20MB limit', () => {
const src = readFileSync(
resolve(__dirname, '../../src/components/KeyframeUpload.tsx'),
'utf-8'
);
expect(src).toContain('20 * 1024 * 1024');
expect(src).toContain('图片文件不能超过20MB');
});
});
describe('Original BUG-001: canSubmit selector (was stale reference)', () => {
it('Toolbar should call canSubmit() as invocation, not reference', () => {
const src = readFileSync(
resolve(__dirname, '../../src/components/Toolbar.tsx'),
'utf-8'
);
// Must be s.canSubmit() — the function call, not s.canSubmit (reference)
expect(src).toMatch(/s\.canSubmit\(\)/);
});
});
describe('Original BUG-002: drag-drop audio filter', () => {
it('InputBar drag-drop should only accept image/* and video/*', () => {
const src = readFileSync(
resolve(__dirname, '../../src/components/InputBar.tsx'),
'utf-8'
);
// Filter should only allow image and video, not audio
expect(src).toContain("f.type.startsWith('image/')");
expect(src).toContain("f.type.startsWith('video/')");
});
});
});
describe('File Upload Validation — Store Level', () => {
beforeEach(() => {
useInputBarStore.getState().reset();
});
it('should accept image files in universal mode', () => {
const file = createMockFile('photo.jpg', 'image/jpeg');
useInputBarStore.getState().addReferences([file]);
expect(useInputBarStore.getState().references).toHaveLength(1);
expect(useInputBarStore.getState().references[0].type).toBe('image');
});
it('should accept video files in universal mode', () => {
const file = createMockFile('clip.mp4', 'video/mp4');
useInputBarStore.getState().addReferences([file]);
expect(useInputBarStore.getState().references).toHaveLength(1);
expect(useInputBarStore.getState().references[0].type).toBe('video');
});
it('should accept only image files for first/last frames', () => {
useInputBarStore.getState().switchMode('keyframe');
const file = createMockFile('frame.jpg', 'image/jpeg');
useInputBarStore.getState().setFirstFrame(file);
expect(useInputBarStore.getState().firstFrame).not.toBeNull();
expect(useInputBarStore.getState().firstFrame!.type).toBe('image');
});
it('keyframe file input should only accept image/*', () => {
const src = readFileSync(
resolve(__dirname, '../../src/components/KeyframeUpload.tsx'),
'utf-8'
);
// Both inputs should have accept="image/*"
const matches = src.match(/accept="image\/\*"/g);
expect(matches).not.toBeNull();
expect(matches!.length).toBe(2);
});
});
describe('PRD Compliance — Code Structure Checks', () => {
it('should have file upload accept limited to image/video in UniversalUpload', () => {
const src = readFileSync(
resolve(__dirname, '../../src/components/UniversalUpload.tsx'),
'utf-8'
);
expect(src).toContain('accept="image/*,video/*"');
});
it('should have URL.revokeObjectURL calls for memory cleanup', () => {
const storeSrc = readFileSync(
resolve(__dirname, '../../src/store/inputBar.ts'),
'utf-8'
);
const revokeCount = (storeSrc.match(/URL\.revokeObjectURL/g) || []).length;
// Should have revokeObjectURL in: removeReference, clearReferences, setFirstFrame, setLastFrame, switchMode(x2), reset
expect(revokeCount).toBeGreaterThanOrEqual(5);
});
it('should preserve prompt text across mode switches', () => {
useInputBarStore.getState().setPrompt('my test prompt');
useInputBarStore.getState().switchMode('keyframe');
expect(useInputBarStore.getState().prompt).toBe('my test prompt');
useInputBarStore.getState().switchMode('universal');
expect(useInputBarStore.getState().prompt).toBe('my test prompt');
});
});
describe('Dead Code Audit — Audio Dead Code Cleaned Up', () => {
it('types/index.ts UploadedFile.type no longer includes audio', () => {
const src = readFileSync(
resolve(__dirname, '../../src/types/index.ts'),
'utf-8'
);
// Audio type was cleaned up in a previous dev session
const hasAudioType = src.includes("'audio'");
expect(hasAudioType).toBe(false); // Verified: audio dead code has been removed
});
it('inputBar.ts addReferences no longer classifies audio files', () => {
const src = readFileSync(
resolve(__dirname, '../../src/store/inputBar.ts'),
'utf-8'
);
const hasAudioClassification = src.includes("'audio'");
expect(hasAudioClassification).toBe(false); // Verified: audio dead code has been removed
});
});