diff --git a/web/app/components/workflow/run/__tests__/output-panel.spec.tsx b/web/app/components/workflow/run/__tests__/output-panel.spec.tsx index bd0613cd591..09e6247d904 100644 --- a/web/app/components/workflow/run/__tests__/output-panel.spec.tsx +++ b/web/app/components/workflow/run/__tests__/output-panel.spec.tsx @@ -21,8 +21,16 @@ vi.mock('@/app/components/base/markdown', () => ({ })) vi.mock('@/app/components/workflow/run/status-container', () => ({ - default: ({ status, children }: { status: string; children?: React.ReactNode }) => ( -
+ default: ({ + status, + children, + copyContent, + }: { + status: string + children?: React.ReactNode + copyContent?: string + }) => ( +
{children}
), @@ -62,6 +70,10 @@ describe('OutputPanel', () => { render() expect(screen.getByTestId('status-container')).toHaveAttribute('data-status', 'failed') + expect(screen.getByTestId('status-container')).toHaveAttribute( + 'data-copy-content', + 'Execution failed', + ) expect(screen.getByText('Execution failed')).toBeInTheDocument() }) diff --git a/web/app/components/workflow/run/__tests__/status-container.spec.tsx b/web/app/components/workflow/run/__tests__/status-container.spec.tsx index 36b44fef80a..b27dc19328a 100644 --- a/web/app/components/workflow/run/__tests__/status-container.spec.tsx +++ b/web/app/components/workflow/run/__tests__/status-container.spec.tsx @@ -1,8 +1,18 @@ import { render, screen } from '@testing-library/react' +import userEvent from '@testing-library/user-event' import useTheme from '@/hooks/use-theme' import { Theme } from '@/types/app' import StatusContainer from '../status-container' +const copy = vi.fn() + +vi.mock('foxact/use-clipboard', () => ({ + useClipboard: () => ({ + copied: false, + copy, + }), +})) + vi.mock('@/hooks/use-theme', () => ({ default: vi.fn(), })) @@ -34,4 +44,19 @@ describe('StatusContainer', () => { ).toBeInTheDocument() }) }) + + it('copies the supplied content from the status action', async () => { + const user = userEvent.setup() + render( + + Execution failed + , + ) + + await user.click( + screen.getByRole('button', { name: 'appOverview.overview.appInfo.embedded.copy' }), + ) + + expect(copy).toHaveBeenCalledWith('Execution failed') + }) }) diff --git a/web/app/components/workflow/run/output-panel.tsx b/web/app/components/workflow/run/output-panel.tsx index 4fedf87956f..1c1c5463ab5 100644 --- a/web/app/components/workflow/run/output-panel.tsx +++ b/web/app/components/workflow/run/output-panel.tsx @@ -53,7 +53,9 @@ const OutputPanel: FC = ({ isRunning, outputs, error, height } )} {!isRunning && error && (
- {error} + + {error} +
)} {!isRunning && !outputs && ( diff --git a/web/app/components/workflow/run/status-container.tsx b/web/app/components/workflow/run/status-container.tsx index 9ed9668b062..8a7038d4870 100644 --- a/web/app/components/workflow/run/status-container.tsx +++ b/web/app/components/workflow/run/status-container.tsx @@ -1,22 +1,26 @@ 'use client' import type { FC } from 'react' import { cn } from '@langgenius/dify-ui/cn' +import CopyFeedback from '@/app/components/base/copy-feedback' import useTheme from '@/hooks/use-theme' import { Theme } from '@/types/app' type Props = { readonly status: string readonly children?: React.ReactNode + readonly copyContent?: string } -const StatusContainer: FC = ({ status, children }) => { +const StatusContainer: FC = ({ status, children, copyContent }) => { const { theme } = useTheme() + const isCopyable = copyContent !== undefined return (
= ({ status, children }) => { >
{children} + {isCopyable && ( +
+ +
+ )}
) }