dify/web/app/components/workflow-app/components/workflow-header/index.spec.tsx

165 lines
5.1 KiB
TypeScript

import type { HeaderProps } from '@/app/components/workflow/header'
import type { App } from '@/types/app'
import { fireEvent, render, screen } from '@testing-library/react'
import { AppModeEnum } from '@/types/app'
import WorkflowHeader from './index'
const mockUseAppStoreSelector = vi.fn()
const mockSetCurrentLogItem = vi.fn()
const mockSetShowMessageLogModal = vi.fn()
const mockResetWorkflowVersionHistory = vi.fn()
const createMockApp = (overrides: Partial<App> = {}): App => ({
id: 'app-id',
name: 'Workflow App',
description: 'Workflow app description',
author_name: 'Workflow app author',
icon_type: 'emoji',
icon: 'app-icon',
icon_background: '#FFFFFF',
icon_url: null,
use_icon_as_answer_icon: false,
mode: AppModeEnum.COMPLETION,
enable_site: true,
enable_api: true,
api_rpm: 60,
api_rph: 3600,
is_demo: false,
model_config: {} as App['model_config'],
app_model_config: {} as App['app_model_config'],
created_at: 0,
updated_at: 0,
site: {
access_token: 'token',
app_base_url: 'https://example.com',
} as App['site'],
api_base_url: 'https://api.example.com',
tags: [],
access_mode: 'public_access' as App['access_mode'],
...overrides,
})
let appDetail: App
const mockAppStore = (overrides: Partial<App> = {}) => {
appDetail = createMockApp(overrides)
mockUseAppStoreSelector.mockImplementation(selector => selector({
appDetail,
setCurrentLogItem: mockSetCurrentLogItem,
setShowMessageLogModal: mockSetShowMessageLogModal,
}))
}
vi.mock('@/app/components/app/store', () => ({
__esModule: true,
useStore: (selector: (state: { appDetail?: App, setCurrentLogItem: typeof mockSetCurrentLogItem, setShowMessageLogModal: typeof mockSetShowMessageLogModal }) => unknown) => mockUseAppStoreSelector(selector),
}))
vi.mock('@/app/components/workflow/header', () => ({
__esModule: true,
default: (props: HeaderProps) => {
return (
<div
data-testid="workflow-header"
data-show-run={String(Boolean(props.normal?.runAndHistoryProps?.showRunButton))}
data-show-preview={String(Boolean(props.normal?.runAndHistoryProps?.showPreviewButton))}
data-history-url={props.normal?.runAndHistoryProps?.viewHistoryProps?.historyUrl ?? ''}
>
<button
type="button"
onClick={() => props.normal?.runAndHistoryProps?.viewHistoryProps?.onClearLogAndMessageModal?.()}
>
clear-history
</button>
<button
type="button"
onClick={() => props.restoring?.onRestoreSettled?.()}
>
restore-settled
</button>
</div>
)
},
}))
vi.mock('@/service/use-workflow', () => ({
__esModule: true,
useResetWorkflowVersionHistory: () => mockResetWorkflowVersionHistory,
}))
describe('WorkflowHeader', () => {
beforeEach(() => {
vi.clearAllMocks()
mockAppStore()
})
// Verifies the wrapper renders the workflow header shell.
describe('Rendering', () => {
it('should render without crashing', () => {
// Act
render(<WorkflowHeader />)
// Assert
expect(screen.getByTestId('workflow-header')).toBeInTheDocument()
})
})
// Verifies chat mode affects which primary action is shown in the header.
describe('Props', () => {
it('should configure preview mode when app is in advanced chat mode', () => {
// Arrange
mockAppStore({ mode: AppModeEnum.ADVANCED_CHAT })
// Act
render(<WorkflowHeader />)
// Assert
const header = screen.getByTestId('workflow-header')
expect(header).toHaveAttribute('data-show-run', 'false')
expect(header).toHaveAttribute('data-show-preview', 'true')
expect(header).toHaveAttribute('data-history-url', '/apps/app-id/advanced-chat/workflow-runs')
})
it('should configure run mode when app is not in advanced chat mode', () => {
// Arrange
mockAppStore({ mode: AppModeEnum.COMPLETION })
// Act
render(<WorkflowHeader />)
// Assert
const header = screen.getByTestId('workflow-header')
expect(header).toHaveAttribute('data-show-run', 'true')
expect(header).toHaveAttribute('data-show-preview', 'false')
expect(header).toHaveAttribute('data-history-url', '/apps/app-id/workflow-runs')
})
})
// Verifies callbacks clear log state as expected.
describe('User Interactions', () => {
it('should clear log and close message modal when clearing history modal state', () => {
// Arrange
render(<WorkflowHeader />)
// Act
fireEvent.click(screen.getByRole('button', { name: /clear-history/i }))
// Assert
expect(mockSetCurrentLogItem).toHaveBeenCalledWith()
expect(mockSetShowMessageLogModal).toHaveBeenCalledWith(false)
})
})
// Ensures restoring callback is wired to reset version history.
describe('Edge Cases', () => {
it('should use resetWorkflowVersionHistory as restore settled handler', () => {
// Act
render(<WorkflowHeader />)
// Assert
fireEvent.click(screen.getByRole('button', { name: /restore-settled/i }))
expect(mockResetWorkflowVersionHistory).toHaveBeenCalled()
})
})
})