feat: 收口组件样式并调整移动端侧栏
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import {
|
||||
directionLabel,
|
||||
mergeDesignedShots,
|
||||
referenceImageUrl,
|
||||
storyboardPrerequisites
|
||||
} from '@/features/storyboard/model'
|
||||
import {
|
||||
directionsResult,
|
||||
episodeShots,
|
||||
storyboardCheckpoint,
|
||||
visualStatesResult
|
||||
} from '@/features/storyboard/testing/fixtures'
|
||||
|
||||
afterEach(() => vi.unstubAllEnvs())
|
||||
|
||||
describe('分镜正式数据与生成依赖', () => {
|
||||
it('用 Shot ID 关联状态,用 Beat + Shot 编号补充描述,不串同编号镜头', () => {
|
||||
const directions = directionsResult('p')
|
||||
const states = visualStatesResult('p', 1, true)
|
||||
states.beats.reverse()
|
||||
states.beats[0]!.shots[0]!.visualState!.continuityNote = '第二个 Beat'
|
||||
const shots = mergeDesignedShots(directions, states, episodeShots())
|
||||
expect(shots.map(shot => [shot.shotId, shot.title, shot.visualState?.continuityNote])).toEqual([
|
||||
['shot-db-1-1', '第1集镜头1', '信封始终在右手'],
|
||||
['shot-db-1-2', '第1集镜头2', '第二个 Beat']
|
||||
])
|
||||
})
|
||||
|
||||
it('没有 Direction 的正式 Shot 仍可显示,不用 checkpoint 伪造数据库 ID', () => {
|
||||
const rows = mergeDesignedShots(directionsResult('p', 1, false), visualStatesResult('p'), episodeShots())
|
||||
expect(rows).toHaveLength(2)
|
||||
expect(rows[0]?.direction).toBeNull()
|
||||
expect(mergeDesignedShots(null, null, episodeShots())).toEqual([])
|
||||
})
|
||||
|
||||
it('仅最新 Breakdown 决定生成前置条件,不能回退到历史完整快照', () => {
|
||||
const ready = storyboardCheckpoint()
|
||||
expect(storyboardPrerequisites([ready], 1)).toMatchObject({ directionEpisode: true, visualEpisode: true })
|
||||
expect(storyboardPrerequisites([ready], 3)).toMatchObject({ directionEpisode: false, visualEpisode: false })
|
||||
const latest = { ...ready, checkpointId: 'failed', createdAt: '2026-08-28T01:00:00Z', state: {} }
|
||||
expect(storyboardPrerequisites([latest, ready], 1)).toMatchObject({
|
||||
directionProject: false,
|
||||
visualProject: false
|
||||
})
|
||||
expect(storyboardPrerequisites([{ ...latest, workflowName: 'create-drama' }, ready], 1)).toMatchObject({
|
||||
directionProject: true
|
||||
})
|
||||
})
|
||||
|
||||
it('保留后端嵌套 Direction 与顶层 VisualState 的不同依赖', () => {
|
||||
const checkpoint = storyboardCheckpoint()
|
||||
delete checkpoint.state.breakdownResult
|
||||
expect(storyboardPrerequisites([checkpoint], 1)).toMatchObject({ directionEpisode: false, visualEpisode: true })
|
||||
checkpoint.state.subjectForms = []
|
||||
expect(storyboardPrerequisites([checkpoint], 1).visualEpisode).toBe(false)
|
||||
expect(directionLabel('future-camera-mode')).toBe('future-camera-mode')
|
||||
})
|
||||
|
||||
it('参考图只允许 http(s) 或后端 storage 地址,拦截不可信协议和路径逃逸', () => {
|
||||
vi.stubEnv('VITE_API_BASE_URL', 'https://api.example.test/api')
|
||||
expect(referenceImageUrl('/storage/image.png')).toBe('https://api.example.test/storage/image.png')
|
||||
expect(referenceImageUrl('https://images.example.test/a.png')).toBe('https://images.example.test/a.png')
|
||||
for (const value of [
|
||||
'javascript:alert(1)',
|
||||
'data:image/svg+xml,anything',
|
||||
'//evil.test/img',
|
||||
'/storage/../api/projects',
|
||||
'/storage/\\evil.test/img'
|
||||
]) {
|
||||
expect(referenceImageUrl(value)).toBeNull()
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user