diff --git a/web/app/components/app/app-publisher/__tests__/index.spec.tsx b/web/app/components/app/app-publisher/__tests__/index.spec.tsx
index a7fad12a27..86b45a2a79 100644
--- a/web/app/components/app/app-publisher/__tests__/index.spec.tsx
+++ b/web/app/components/app/app-publisher/__tests__/index.spec.tsx
@@ -657,4 +657,178 @@ describe('AppPublisher', () => {
expect(sectionProps.summary?.workflowTypeSwitchDisabled).toBe(true)
expect(sectionProps.summary?.workflowTypeSwitchDisabledReason).toBe('common.switchToEvaluationWorkflowDisabledTip')
})
+
+ it('should switch workflow type, refresh app detail, and close the popover for published apps', async () => {
+ mockFetchAppDetailDirect.mockResolvedValueOnce({
+ id: 'app-1',
+ type: AppTypeEnum.EVALUATION,
+ })
+
+ render(
+ ,
+ )
+
+ fireEvent.click(screen.getByText('common.publish'))
+ fireEvent.click(screen.getByText('publisher-switch-workflow-type'))
+
+ await waitFor(() => {
+ expect(mockConvertWorkflowType).toHaveBeenCalledWith({
+ params: { appId: 'app-1' },
+ query: { target_type: AppTypeEnum.EVALUATION },
+ })
+ expect(mockFetchAppDetailDirect).toHaveBeenCalledWith({ url: '/apps', id: 'app-1' })
+ expect(mockSetAppDetail).toHaveBeenCalledWith({
+ id: 'app-1',
+ type: AppTypeEnum.EVALUATION,
+ })
+ })
+ expect(screen.queryByText('publisher-summary-publish')).not.toBeInTheDocument()
+ })
+
+ it('should hide access and actions sections for evaluation workflow apps', () => {
+ mockAppDetail = {
+ ...mockAppDetail,
+ type: AppTypeEnum.EVALUATION,
+ }
+
+ render(
+ ,
+ )
+
+ fireEvent.click(screen.getByText('common.publish'))
+
+ expect(screen.getByText('publisher-summary-publish')).toBeInTheDocument()
+ expect(screen.queryByText('publisher-access-control')).not.toBeInTheDocument()
+ expect(screen.queryByText('publisher-embed')).not.toBeInTheDocument()
+ expect(sectionProps.summary?.workflowTypeSwitchConfig).toEqual({
+ targetType: AppTypeEnum.WORKFLOW,
+ publishLabelKey: 'common.publishAsStandardWorkflow',
+ switchLabelKey: 'common.switchToStandardWorkflow',
+ tipKey: 'common.switchToStandardWorkflowTip',
+ })
+ })
+
+ it('should confirm before switching an evaluation workflow with associated targets to a standard workflow', async () => {
+ mockAppDetail = {
+ ...mockAppDetail,
+ type: AppTypeEnum.EVALUATION,
+ }
+ mockEvaluationWorkflowAssociatedTargets = {
+ items: [
+ {
+ target_type: 'app',
+ target_id: 'dependent-app-1',
+ target_name: 'Dependent App',
+ },
+ {
+ target_type: 'knowledge_base',
+ target_id: 'knowledge-1',
+ target_name: 'Knowledge Base',
+ },
+ ],
+ }
+ mockRefetchEvaluationWorkflowAssociatedTargets.mockResolvedValueOnce({
+ data: mockEvaluationWorkflowAssociatedTargets,
+ isError: false,
+ })
+
+ render(
+ ,
+ )
+
+ fireEvent.click(screen.getByText('common.publish'))
+ fireEvent.click(screen.getByText('publisher-switch-workflow-type'))
+
+ await waitFor(() => {
+ expect(mockRefetchEvaluationWorkflowAssociatedTargets).toHaveBeenCalledTimes(1)
+ })
+ expect(mockConvertWorkflowType).not.toHaveBeenCalled()
+ expect(screen.getByText('Dependent App')).toBeInTheDocument()
+ expect(screen.getByText('Knowledge Base')).toBeInTheDocument()
+
+ fireEvent.click(screen.getByRole('button', { name: 'common.switchToStandardWorkflowConfirm.switch' }))
+
+ await waitFor(() => {
+ expect(mockConvertWorkflowType).toHaveBeenCalledWith({
+ params: { appId: 'app-1' },
+ query: { target_type: AppTypeEnum.WORKFLOW },
+ })
+ })
+ })
+
+ it('should switch an evaluation workflow directly when there are no associated targets', async () => {
+ mockAppDetail = {
+ ...mockAppDetail,
+ type: AppTypeEnum.EVALUATION,
+ }
+
+ render(
+ ,
+ )
+
+ fireEvent.click(screen.getByText('common.publish'))
+ fireEvent.click(screen.getByText('publisher-switch-workflow-type'))
+
+ await waitFor(() => {
+ expect(mockRefetchEvaluationWorkflowAssociatedTargets).toHaveBeenCalledTimes(1)
+ expect(mockConvertWorkflowType).toHaveBeenCalledWith({
+ params: { appId: 'app-1' },
+ query: { target_type: AppTypeEnum.WORKFLOW },
+ })
+ })
+ expect(screen.queryByText('common.switchToStandardWorkflowConfirm.title')).not.toBeInTheDocument()
+ })
+
+ it('should block switching an evaluation workflow when associated targets fail to load', async () => {
+ mockAppDetail = {
+ ...mockAppDetail,
+ type: AppTypeEnum.EVALUATION,
+ }
+ mockRefetchEvaluationWorkflowAssociatedTargets.mockResolvedValueOnce({
+ data: undefined,
+ isError: true,
+ })
+
+ render(
+ ,
+ )
+
+ fireEvent.click(screen.getByText('common.publish'))
+ fireEvent.click(screen.getByText('publisher-switch-workflow-type'))
+
+ await waitFor(() => {
+ expect(mockToastError).toHaveBeenCalledWith('common.switchToStandardWorkflowConfirm.loadFailed')
+ })
+ expect(mockConvertWorkflowType).not.toHaveBeenCalled()
+ })
+
+ it('should block switching to evaluation workflow when restricted nodes exist', async () => {
+ render(
+ ,
+ )
+
+ fireEvent.click(screen.getByText('common.publish'))
+ fireEvent.click(screen.getByText('publisher-switch-workflow-type'))
+
+ await waitFor(() => {
+ expect(mockToastError).toHaveBeenCalledWith('common.switchToEvaluationWorkflowDisabledTip')
+ })
+
+ expect(mockConvertWorkflowType).not.toHaveBeenCalled()
+ expect(sectionProps.summary?.workflowTypeSwitchDisabled).toBe(true)
+ expect(sectionProps.summary?.workflowTypeSwitchDisabledReason).toBe('common.switchToEvaluationWorkflowDisabledTip')
+ })
})