mirror of https://github.com/langgenius/dify.git
306 lines
8.4 KiB
TypeScript
306 lines
8.4 KiB
TypeScript
import type { FileEntity } from './types'
|
|
import { act, render, screen } from '@testing-library/react'
|
|
import { describe, expect, it, vi } from 'vitest'
|
|
import {
|
|
createFileStore,
|
|
FileContextProvider,
|
|
useFileStore,
|
|
useFileStoreWithSelector,
|
|
} from './store'
|
|
|
|
const createMockFile = (id: string): FileEntity => ({
|
|
id,
|
|
name: `file-${id}.png`,
|
|
size: 1024,
|
|
extension: 'png',
|
|
mimeType: 'image/png',
|
|
progress: 0,
|
|
})
|
|
|
|
describe('image-uploader store', () => {
|
|
describe('createFileStore', () => {
|
|
it('should create store with empty array by default', () => {
|
|
const store = createFileStore()
|
|
expect(store.getState().files).toEqual([])
|
|
})
|
|
|
|
it('should create store with initial value', () => {
|
|
const initialFiles = [createMockFile('1'), createMockFile('2')]
|
|
const store = createFileStore(initialFiles)
|
|
expect(store.getState().files).toHaveLength(2)
|
|
})
|
|
|
|
it('should create copy of initial value', () => {
|
|
const initialFiles = [createMockFile('1')]
|
|
const store = createFileStore(initialFiles)
|
|
store.getState().files.push(createMockFile('2'))
|
|
expect(initialFiles).toHaveLength(1)
|
|
})
|
|
|
|
it('should update files with setFiles', () => {
|
|
const store = createFileStore()
|
|
const newFiles = [createMockFile('1'), createMockFile('2')]
|
|
|
|
act(() => {
|
|
store.getState().setFiles(newFiles)
|
|
})
|
|
|
|
expect(store.getState().files).toEqual(newFiles)
|
|
})
|
|
|
|
it('should call onChange when setFiles is called', () => {
|
|
const onChange = vi.fn()
|
|
const store = createFileStore([], onChange)
|
|
const newFiles = [createMockFile('1')]
|
|
|
|
act(() => {
|
|
store.getState().setFiles(newFiles)
|
|
})
|
|
|
|
expect(onChange).toHaveBeenCalledWith(newFiles)
|
|
})
|
|
|
|
it('should not throw when onChange is not provided', () => {
|
|
const store = createFileStore([])
|
|
const newFiles = [createMockFile('1')]
|
|
|
|
expect(() => {
|
|
act(() => {
|
|
store.getState().setFiles(newFiles)
|
|
})
|
|
}).not.toThrow()
|
|
})
|
|
|
|
it('should handle undefined initial value', () => {
|
|
const store = createFileStore(undefined)
|
|
expect(store.getState().files).toEqual([])
|
|
})
|
|
|
|
it('should handle null-like falsy value with empty array fallback', () => {
|
|
// Test the ternary: value ? [...value] : []
|
|
const store = createFileStore(null as unknown as FileEntity[])
|
|
expect(store.getState().files).toEqual([])
|
|
})
|
|
|
|
it('should handle empty array as initial value', () => {
|
|
const store = createFileStore([])
|
|
expect(store.getState().files).toEqual([])
|
|
})
|
|
})
|
|
|
|
describe('FileContextProvider', () => {
|
|
it('should render children', () => {
|
|
render(
|
|
<FileContextProvider>
|
|
<div>Test Child</div>
|
|
</FileContextProvider>,
|
|
)
|
|
expect(screen.getByText('Test Child')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should provide store to children', () => {
|
|
const TestComponent = () => {
|
|
const store = useFileStore()
|
|
// useFileStore returns a store that's truthy by design
|
|
return <div data-testid="store-exists">{store !== null ? 'yes' : 'no'}</div>
|
|
}
|
|
|
|
render(
|
|
<FileContextProvider>
|
|
<TestComponent />
|
|
</FileContextProvider>,
|
|
)
|
|
|
|
expect(screen.getByTestId('store-exists')).toHaveTextContent('yes')
|
|
})
|
|
|
|
it('should initialize store with value prop', () => {
|
|
const initialFiles = [createMockFile('1')]
|
|
|
|
const TestComponent = () => {
|
|
const store = useFileStore()
|
|
return <div data-testid="file-count">{store.getState().files.length}</div>
|
|
}
|
|
|
|
render(
|
|
<FileContextProvider value={initialFiles}>
|
|
<TestComponent />
|
|
</FileContextProvider>,
|
|
)
|
|
|
|
expect(screen.getByTestId('file-count')).toHaveTextContent('1')
|
|
})
|
|
|
|
it('should call onChange when files change', () => {
|
|
const onChange = vi.fn()
|
|
const newFiles = [createMockFile('1')]
|
|
|
|
const TestComponent = () => {
|
|
const store = useFileStore()
|
|
return (
|
|
<button onClick={() => store.getState().setFiles(newFiles)}>
|
|
Set Files
|
|
</button>
|
|
)
|
|
}
|
|
|
|
render(
|
|
<FileContextProvider onChange={onChange}>
|
|
<TestComponent />
|
|
</FileContextProvider>,
|
|
)
|
|
|
|
act(() => {
|
|
screen.getByRole('button').click()
|
|
})
|
|
|
|
expect(onChange).toHaveBeenCalledWith(newFiles)
|
|
})
|
|
|
|
it('should reuse existing store on re-render (storeRef.current already exists)', () => {
|
|
const initialFiles = [createMockFile('1')]
|
|
let renderCount = 0
|
|
|
|
const TestComponent = () => {
|
|
const store = useFileStore()
|
|
renderCount++
|
|
return (
|
|
<div>
|
|
<span data-testid="file-count">{store.getState().files.length}</span>
|
|
<span data-testid="render-count">{renderCount}</span>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const { rerender } = render(
|
|
<FileContextProvider value={initialFiles}>
|
|
<TestComponent />
|
|
</FileContextProvider>,
|
|
)
|
|
|
|
expect(screen.getByTestId('file-count')).toHaveTextContent('1')
|
|
|
|
// Re-render the provider - should reuse the same store
|
|
rerender(
|
|
<FileContextProvider value={initialFiles}>
|
|
<TestComponent />
|
|
</FileContextProvider>,
|
|
)
|
|
|
|
// Store should still have the same files (store was reused)
|
|
expect(screen.getByTestId('file-count')).toHaveTextContent('1')
|
|
expect(renderCount).toBeGreaterThan(1)
|
|
})
|
|
})
|
|
|
|
describe('useFileStore', () => {
|
|
it('should return store from context', () => {
|
|
const TestComponent = () => {
|
|
const store = useFileStore()
|
|
// useFileStore returns a store that's truthy by design
|
|
return <div data-testid="result">{store !== null ? 'has store' : 'no store'}</div>
|
|
}
|
|
|
|
render(
|
|
<FileContextProvider>
|
|
<TestComponent />
|
|
</FileContextProvider>,
|
|
)
|
|
|
|
expect(screen.getByTestId('result')).toHaveTextContent('has store')
|
|
})
|
|
})
|
|
|
|
describe('useFileStoreWithSelector', () => {
|
|
it('should throw error when used outside provider', () => {
|
|
const TestComponent = () => {
|
|
try {
|
|
useFileStoreWithSelector(state => state.files)
|
|
return <div>No Error</div>
|
|
}
|
|
catch {
|
|
return <div>Error</div>
|
|
}
|
|
}
|
|
|
|
render(<TestComponent />)
|
|
expect(screen.getByText('Error')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should select files from store', () => {
|
|
const initialFiles = [createMockFile('1'), createMockFile('2')]
|
|
|
|
const TestComponent = () => {
|
|
const files = useFileStoreWithSelector(state => state.files)
|
|
return <div data-testid="files-count">{files.length}</div>
|
|
}
|
|
|
|
render(
|
|
<FileContextProvider value={initialFiles}>
|
|
<TestComponent />
|
|
</FileContextProvider>,
|
|
)
|
|
|
|
expect(screen.getByTestId('files-count')).toHaveTextContent('2')
|
|
})
|
|
|
|
it('should select setFiles function from store', () => {
|
|
const onChange = vi.fn()
|
|
|
|
const TestComponent = () => {
|
|
const setFiles = useFileStoreWithSelector(state => state.setFiles)
|
|
return (
|
|
<button onClick={() => setFiles([createMockFile('new')])}>
|
|
Update
|
|
</button>
|
|
)
|
|
}
|
|
|
|
render(
|
|
<FileContextProvider onChange={onChange}>
|
|
<TestComponent />
|
|
</FileContextProvider>,
|
|
)
|
|
|
|
act(() => {
|
|
screen.getByRole('button').click()
|
|
})
|
|
|
|
expect(onChange).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should re-render when selected state changes', () => {
|
|
const renderCount = { current: 0 }
|
|
|
|
const TestComponent = () => {
|
|
const files = useFileStoreWithSelector(state => state.files)
|
|
const setFiles = useFileStoreWithSelector(state => state.setFiles)
|
|
renderCount.current++
|
|
|
|
return (
|
|
<div>
|
|
<span data-testid="count">{files.length}</span>
|
|
<button onClick={() => setFiles([...files, createMockFile('new')])}>
|
|
Add
|
|
</button>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
render(
|
|
<FileContextProvider>
|
|
<TestComponent />
|
|
</FileContextProvider>,
|
|
)
|
|
|
|
expect(screen.getByTestId('count')).toHaveTextContent('0')
|
|
|
|
act(() => {
|
|
screen.getByRole('button').click()
|
|
})
|
|
|
|
expect(screen.getByTestId('count')).toHaveTextContent('1')
|
|
})
|
|
})
|
|
})
|