cleanup
This commit is contained in:
parent
0ae09c9df5
commit
0274fb6626
|
|
@ -7,8 +7,6 @@
|
|||
"dev": "next dev --turbopack",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"test": "vitest",
|
||||
"test:run": "vitest run",
|
||||
"lint": "biome check src/",
|
||||
"lint:fix": "biome check src/ --write",
|
||||
"format": "biome format src/ --write",
|
||||
|
|
@ -76,9 +74,6 @@
|
|||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.11",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@testing-library/jest-dom": "^6.4.0",
|
||||
"@testing-library/react": "^15.0.7",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/bun": "latest",
|
||||
"@types/node": "^24.2.1",
|
||||
"@types/pg": "^8.15.4",
|
||||
|
|
@ -86,11 +81,9 @@
|
|||
"@types/react-dom": "^18.2.18",
|
||||
"cross-env": "^7.0.3",
|
||||
"drizzle-kit": "^0.31.4",
|
||||
"jsdom": "^26.0.0",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"tsx": "^4.7.1",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^2.1.8"
|
||||
"typescript": "^5.8.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,217 +0,0 @@
|
|||
import React from 'react'
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
||||
import { render, screen, fireEvent } from '@testing-library/react'
|
||||
import { Timeline } from '../index'
|
||||
|
||||
// Mock the stores and hooks
|
||||
vi.mock('@/stores/timeline-store', () => ({
|
||||
useTimelineStore: () => ({
|
||||
tracks: [],
|
||||
addTrack: vi.fn(),
|
||||
addElementToTrack: vi.fn(),
|
||||
removeElementFromTrack: vi.fn(),
|
||||
removeElementFromTrackWithRipple: vi.fn(),
|
||||
selectedElements: [],
|
||||
clearSelectedElements: vi.fn(),
|
||||
splitElement: vi.fn(),
|
||||
splitAndKeepLeft: vi.fn(),
|
||||
splitAndKeepRight: vi.fn(),
|
||||
separateAudio: vi.fn(),
|
||||
snappingEnabled: false,
|
||||
toggleSnapping: vi.fn(),
|
||||
deleteElements: vi.fn(),
|
||||
duplicateElement: vi.fn(),
|
||||
selectAll: vi.fn(),
|
||||
zoomLevel: 1,
|
||||
setZoomLevel: vi.fn(),
|
||||
dragState: { isDragging: false, draggedElement: null, dragOffset: { x: 0, y: 0 } },
|
||||
getTotalDuration: () => 100,
|
||||
setSelectedElements: vi.fn(),
|
||||
toggleTrackMute: vi.fn(),
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/media-store', () => ({
|
||||
useMediaStore: () => ({
|
||||
mediaItems: [],
|
||||
addMediaItem: vi.fn(),
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/stores/project-store', () => ({
|
||||
useProjectStore: () => ({
|
||||
activeProject: null,
|
||||
})
|
||||
}))
|
||||
|
||||
const mockSeek = vi.fn()
|
||||
|
||||
vi.mock('@/stores/playback-store', () => ({
|
||||
usePlaybackStore: () => ({
|
||||
currentTime: 0,
|
||||
duration: 100,
|
||||
seek: mockSeek,
|
||||
setDuration: vi.fn(),
|
||||
isPlaying: false,
|
||||
toggle: vi.fn(),
|
||||
}),
|
||||
}))
|
||||
|
||||
// Mock the timeline playhead hook
|
||||
vi.mock('@/hooks/use-timeline-playhead', () => ({
|
||||
useTimelinePlayhead: () => ({
|
||||
playheadRef: { current: null },
|
||||
updatePlayheadPosition: vi.fn(),
|
||||
onPlayheadDrag: vi.fn(),
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/use-editor-actions', () => ({
|
||||
useEditorActions: () => ({})
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/use-timeline-context-menu', () => ({
|
||||
useTimelineContextMenu: () => ({
|
||||
contextMenu: null,
|
||||
handleContextMenu: vi.fn(),
|
||||
closeContextMenu: vi.fn(),
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/use-timeline-selection', () => ({
|
||||
useTimelineSelection: () => ({
|
||||
isSelecting: false,
|
||||
selectionBox: null,
|
||||
handleMouseDown: vi.fn(),
|
||||
})
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/use-timeline-drag-drop', () => ({
|
||||
useTimelineDragDrop: () => ({
|
||||
dragProps: {},
|
||||
})
|
||||
}))
|
||||
|
||||
// Mock UI components to avoid dependency issues
|
||||
vi.mock('../../ui/tooltip', () => ({
|
||||
Tooltip: ({ children }: any) => children,
|
||||
TooltipTrigger: ({ children }: any) => children,
|
||||
TooltipContent: ({ children }: any) => <div>{children}</div>,
|
||||
TooltipProvider: ({ children }: any) => children,
|
||||
}))
|
||||
|
||||
vi.mock('../../ui/button', () => ({
|
||||
Button: ({ children, onClick, ...props }: any) => (
|
||||
<button onClick={onClick} {...props} aria-label={props['aria-label']}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('../../ui/scroll-area', () => ({
|
||||
ScrollArea: ({ children }: any) => <div>{children}</div>,
|
||||
}))
|
||||
|
||||
vi.mock('lucide-react', () => ({
|
||||
SkipBack: () => <svg data-testid="skip-back-icon" />,
|
||||
Play: () => <svg data-testid="play-icon" />,
|
||||
Pause: () => <svg data-testid="pause-icon" />,
|
||||
Scissors: () => <svg />,
|
||||
ArrowLeftToLine: () => <svg />,
|
||||
ArrowRightToLine: () => <svg />,
|
||||
Trash2: () => <svg />,
|
||||
Snowflake: () => <svg />,
|
||||
Copy: () => <svg />,
|
||||
SplitSquareHorizontal: () => <svg />,
|
||||
Video: () => <svg />,
|
||||
Music: () => <svg />,
|
||||
TypeIcon: () => <svg />,
|
||||
Magnet: () => <svg />,
|
||||
Link: () => <svg />,
|
||||
ZoomIn: () => <svg />,
|
||||
ZoomOut: () => <svg />,
|
||||
}))
|
||||
|
||||
vi.mock('@/constants/timeline', () => ({
|
||||
TIMELINE_CONSTANTS: {
|
||||
DEFAULT_TEXT_DURATION: 5,
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('@/lib/utils', () => ({
|
||||
cn: (...args: any[]) => args.join(' '),
|
||||
snapTimeToFrame: (time: number) => time,
|
||||
}))
|
||||
|
||||
// Mock timeline utilities
|
||||
vi.mock('@/lib/timeline-utils', () => ({
|
||||
getTotalDuration: () => 100,
|
||||
getCurrentSnapPoint: () => null,
|
||||
}))
|
||||
|
||||
// Mock context menu components
|
||||
vi.mock('../../ui/context-menu', () => ({
|
||||
ContextMenu: ({ children }: any) => children,
|
||||
ContextMenuTrigger: ({ children }: any) => children,
|
||||
ContextMenuContent: ({ children }: any) => <div>{children}</div>,
|
||||
ContextMenuItem: ({ children }: any) => <div>{children}</div>,
|
||||
ContextMenuSeparator: () => <div />,
|
||||
}))
|
||||
|
||||
// Mock dialog components
|
||||
vi.mock('../../ui/dialog', () => ({
|
||||
Dialog: ({ children }: any) => children,
|
||||
DialogContent: ({ children }: any) => <div>{children}</div>,
|
||||
DialogHeader: ({ children }: any) => <div>{children}</div>,
|
||||
DialogTitle: ({ children }: any) => <div>{children}</div>,
|
||||
}))
|
||||
|
||||
describe('Timeline - Return to Start Button', () => {
|
||||
beforeEach(() => {
|
||||
mockSeek.mockClear()
|
||||
})
|
||||
|
||||
it('should render Return to Start button', () => {
|
||||
render(<Timeline />)
|
||||
|
||||
// Look for the SkipBack icon which indicates our button
|
||||
const skipBackIcon = screen.getByTestId('skip-back-icon')
|
||||
expect(skipBackIcon).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should call seek(0) when Return to Start button is clicked', () => {
|
||||
render(<Timeline />)
|
||||
|
||||
// Find the button that contains the SkipBack icon
|
||||
const skipBackIcon = screen.getByTestId('skip-back-icon')
|
||||
const button = skipBackIcon.closest('button')
|
||||
|
||||
expect(button).toBeInTheDocument()
|
||||
fireEvent.click(button!)
|
||||
|
||||
expect(mockSeek).toHaveBeenCalledWith(0)
|
||||
})
|
||||
|
||||
it('should have tooltip structure for Return to Start button', () => {
|
||||
render(<Timeline />)
|
||||
|
||||
// Find the skip back button and verify it's wrapped in tooltip components
|
||||
const skipBackIcon = screen.getByTestId('skip-back-icon')
|
||||
const button = skipBackIcon.closest('button')
|
||||
|
||||
expect(button).toBeInTheDocument()
|
||||
// The button should exist and be clickable, which means the tooltip structure is in place
|
||||
expect(button).toHaveAttribute('data-state', 'closed') // Tooltip trigger state
|
||||
})
|
||||
|
||||
it('should have SkipBack icon in Return to Start button', () => {
|
||||
render(<Timeline />)
|
||||
|
||||
const skipBackIcon = screen.getByTestId('skip-back-icon')
|
||||
expect(skipBackIcon).toBeInTheDocument()
|
||||
|
||||
// Verify it's inside a button
|
||||
const button = skipBackIcon.closest('button')
|
||||
expect(button).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Button } from "../../ui/button";
|
||||
import {
|
||||
|
|
@ -609,7 +608,11 @@ export function Timeline() {
|
|||
onMouseEnter={() => setIsInTimeline(true)}
|
||||
onMouseLeave={() => setIsInTimeline(false)}
|
||||
>
|
||||
<TimelineToolbar zoomLevel={zoomLevel} setZoomLevel={setZoomLevel} seek={seek} />
|
||||
<TimelineToolbar
|
||||
zoomLevel={zoomLevel}
|
||||
setZoomLevel={setZoomLevel}
|
||||
seek={seek}
|
||||
/>
|
||||
|
||||
{/* Timeline Container */}
|
||||
<div
|
||||
|
|
@ -959,7 +962,6 @@ function TimelineToolbar({
|
|||
const { currentTime, duration, isPlaying, toggle } = usePlaybackStore();
|
||||
const { toggleBookmark, isBookmarked } = useProjectStore();
|
||||
|
||||
// Action handlers
|
||||
const handleSplitSelected = () => {
|
||||
if (selectedElements.length === 0) return;
|
||||
let splitCount = 0;
|
||||
|
|
@ -1075,7 +1077,6 @@ function TimelineToolbar({
|
|||
clearSelectedElements();
|
||||
};
|
||||
|
||||
// Zoom handlers
|
||||
const handleZoomIn = () => {
|
||||
setZoomLevel(Math.min(4, zoomLevel + 0.25));
|
||||
};
|
||||
|
|
@ -1092,13 +1093,11 @@ function TimelineToolbar({
|
|||
await toggleBookmark(currentTime);
|
||||
};
|
||||
|
||||
// Check if the current time is bookmarked
|
||||
const currentBookmarked = isBookmarked(currentTime);
|
||||
return (
|
||||
<div className="border-b flex items-center justify-between px-2 py-1">
|
||||
<div className="flex items-center gap-1 w-full">
|
||||
<TooltipProvider delayDuration={500}>
|
||||
{/* Play/Pause Button */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
|
|
@ -1118,31 +1117,26 @@ function TimelineToolbar({
|
|||
{isPlaying ? "Pause (Space)" : "Play (Space)"}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
{/* Return to Start Button - Seeks timeline to 00:00:00 */}
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="text"
|
||||
size="icon"
|
||||
onClick={() => seek(0)} // Seek to timeline start position
|
||||
onClick={() => seek(0)}
|
||||
className="mr-2"
|
||||
>
|
||||
<SkipBack className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
Return to Start (Home / Enter)
|
||||
</TooltipContent>
|
||||
<TooltipContent>Return to Start (Home / Enter)</TooltipContent>
|
||||
</Tooltip>
|
||||
<div className="w-px h-6 bg-border mx-1" />
|
||||
{/* Time Display */}
|
||||
<div
|
||||
className="text-xs text-muted-foreground font-mono px-2"
|
||||
style={{ minWidth: "18ch", textAlign: "center" }}
|
||||
>
|
||||
{currentTime.toFixed(1)}s / {duration.toFixed(1)}s
|
||||
</div>
|
||||
{/* Test Clip Button - for debugging */}
|
||||
{tracks.length === 0 && (
|
||||
<>
|
||||
<div className="w-px h-6 bg-border mx-1" />
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"use client";
|
||||
|
||||
import React, { useRef, useState, useEffect } from "react";
|
||||
import { useRef, useState, useEffect } from "react";
|
||||
import { TimelineTrack } from "@/types/timeline";
|
||||
import { TIMELINE_CONSTANTS } from "@/constants/timeline-constants";
|
||||
import { useTimelinePlayhead } from "@/hooks/use-timeline-playhead";
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import { renderHook } from '@testing-library/react'
|
||||
import { useKeyboardShortcutsHelp } from '../use-keyboard-shortcuts-help'
|
||||
import { useKeybindingsStore } from '../../stores/keybindings-store'
|
||||
|
||||
describe('useKeyboardShortcutsHelp', () => {
|
||||
beforeEach(() => {
|
||||
// Reset store to default state before each test
|
||||
useKeybindingsStore.getState().resetToDefaults()
|
||||
})
|
||||
|
||||
it('should format Enter key correctly in shortcuts', () => {
|
||||
const { result } = renderHook(() => useKeyboardShortcutsHelp())
|
||||
|
||||
// Find the goto-start action in shortcuts
|
||||
const gotoStartShortcut = result.current.shortcuts.find(
|
||||
shortcut => shortcut.action === 'goto-start'
|
||||
)
|
||||
|
||||
expect(gotoStartShortcut).toBeDefined()
|
||||
expect(gotoStartShortcut?.keys).toContain('Enter')
|
||||
expect(gotoStartShortcut?.keys).toContain('Home')
|
||||
})
|
||||
|
||||
it('should include goto-start action with correct description', () => {
|
||||
const { result } = renderHook(() => useKeyboardShortcutsHelp())
|
||||
|
||||
const gotoStartShortcut = result.current.shortcuts.find(
|
||||
shortcut => shortcut.action === 'goto-start'
|
||||
)
|
||||
|
||||
expect(gotoStartShortcut).toBeDefined()
|
||||
expect(gotoStartShortcut?.description).toBe('Go to timeline start')
|
||||
expect(gotoStartShortcut?.category).toBe('Navigation')
|
||||
})
|
||||
|
||||
it('should have multiple keys for goto-start action', () => {
|
||||
const { result } = renderHook(() => useKeyboardShortcutsHelp())
|
||||
|
||||
const gotoStartShortcut = result.current.shortcuts.find(
|
||||
shortcut => shortcut.action === 'goto-start'
|
||||
)
|
||||
|
||||
expect(gotoStartShortcut?.keys.length).toBeGreaterThanOrEqual(2)
|
||||
})
|
||||
})
|
||||
|
|
@ -77,7 +77,7 @@ const formatKey = (key: string): string => {
|
|||
.replace("down", "↓")
|
||||
.replace("space", "Space")
|
||||
.replace("home", "Home")
|
||||
.replace("enter", "Enter") // Format Enter key for display in help dialog
|
||||
.replace("enter", "Enter")
|
||||
.replace("end", "End")
|
||||
.replace("delete", "Delete")
|
||||
.replace("backspace", "Backspace")
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export const defaultKeybindings: KeybindingConfig = {
|
|||
"shift+left": "jump-backward",
|
||||
"shift+right": "jump-forward",
|
||||
home: "goto-start",
|
||||
enter: "goto-start", // More intuitive alternative to Home key for timeline navigation
|
||||
enter: "goto-start",
|
||||
end: "goto-end",
|
||||
s: "split-element",
|
||||
n: "toggle-snapping",
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
import '@testing-library/jest-dom'
|
||||
|
||||
// Mock ResizeObserver
|
||||
global.ResizeObserver = class ResizeObserver {
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
import { defineConfig } from 'vitest/config'
|
||||
import { resolve } from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
setupFiles: ['./src/test/setup.ts'],
|
||||
globals: true,
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, './src'),
|
||||
},
|
||||
},
|
||||
})
|
||||
Loading…
Reference in New Issue