import type { ReactElement } from 'react'
import type { AppPublisherProps } from '@/app/components/app/app-publisher'
import type { App } from '@/types/app'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useStore as useAppStore } from '@/app/components/app/store'
import { Plan } from '@/app/components/billing/type'
import { BlockEnum, InputVarType } from '@/app/components/workflow/types'
import { consoleQuery } from '@/service/client'
import FeaturesTrigger from '../features-trigger'

const mockUseIsChatMode = vi.fn()
const mockUseTheme = vi.fn()
const mockUseNodesReadOnly = vi.fn()
const mockUseChecklist = vi.fn()
const mockUseChecklistBeforePublish = vi.fn()
const mockUseNodesSyncDraft = vi.fn()
const mockUseFeatures = vi.fn()
const mockUseProviderContext = vi.fn()
const mockUseNodes = vi.fn()
const mockUseEdges = vi.fn()

const toastMocks = vi.hoisted(() => ({
  call: vi.fn(),
  dismiss: vi.fn(),
  update: vi.fn(),
  promise: vi.fn(),
}))

vi.mock('@langgenius/dify-ui/toast', () => ({
  toast: Object.assign(toastMocks.call, {
    success: vi.fn((message: string, options?: Record<string, unknown>) =>
      toastMocks.call({ type: 'success', message, ...options }),
    ),
    error: vi.fn((message: string, options?: Record<string, unknown>) =>
      toastMocks.call({ type: 'error', message, ...options }),
    ),
    warning: vi.fn((message: string, options?: Record<string, unknown>) =>
      toastMocks.call({ type: 'warning', message, ...options }),
    ),
    info: vi.fn((message: string, options?: Record<string, unknown>) =>
      toastMocks.call({ type: 'info', message, ...options }),
    ),
    dismiss: toastMocks.dismiss,
    update: toastMocks.update,
    promise: toastMocks.promise,
  }),
}))
const mockHandleCheckBeforePublish = vi.fn()
const mockHandleSyncWorkflowDraft = vi.fn()
const mockPublishWorkflow = vi.fn()
const mockUpdatePublishedWorkflow = vi.fn()
const mockResetWorkflowVersionHistory = vi.fn()
const mockInvalidateAppTriggers = vi.fn()
const mockFetchAppDetail = vi.fn()
const mockInvalidateQueries = vi.fn()
const mockSetQueryData = vi.fn()
const mockSetPublishedAt = vi.fn()
const mockSetLastPublishedHasUserInput = vi.fn()

const mockWorkflowStoreSetState = vi.fn()
const mockWorkflowStoreSetShowFeaturesPanel = vi.fn()

let workflowStoreState = {
  showFeaturesPanel: false,
  isRestoring: false,
  setShowFeaturesPanel: mockWorkflowStoreSetShowFeaturesPanel,
  setPublishedAt: mockSetPublishedAt,
  setLastPublishedHasUserInput: mockSetLastPublishedHasUserInput,
}

const mockWorkflowStore = {
  getState: () => workflowStoreState,
  setState: mockWorkflowStoreSetState,
}

vi.mock('@/app/components/workflow/hooks/use-workflow', () => ({
  useNodesReadOnly: () => mockUseNodesReadOnly(),
  useIsChatMode: () => mockUseIsChatMode(),
}))

vi.mock('@/app/components/workflow/hooks/use-checklist', () => ({
  useChecklist: (...args: unknown[]) => mockUseChecklist(...args),
  useChecklistBeforePublish: () => mockUseChecklistBeforePublish(),
}))

vi.mock('@/app/components/workflow/hooks/use-nodes-sync-draft', () => ({
  useNodesSyncDraft: () => mockUseNodesSyncDraft(),
}))

vi.mock('@/app/components/workflow/store', () => ({
  useStore: (selector: (state: Record<string, unknown>) => unknown) => {
    const state: Record<string, unknown> = {
      publishedAt: null,
      draftUpdatedAt: null,
      toolPublished: false,
      lastPublishedHasUserInput: false,
    }
    return selector(state)
  },
  useWorkflowStore: () => mockWorkflowStore,
}))

vi.mock('@/app/components/workflow/hooks-store', () => ({
  useHooksStore: <T,>(
    selector: (state: { accessControl: { canReleaseAndVersion: boolean } }) => T,
  ): T =>
    selector({
      accessControl: {
        canReleaseAndVersion: true,
      },
    }),
}))

vi.mock('@tanstack/react-query', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@tanstack/react-query')>()
  return {
    ...actual,
    useQueryClient: () => ({
      invalidateQueries: mockInvalidateQueries,
      setQueryData: mockSetQueryData,
    }),
  }
})

vi.mock('@/app/components/base/features/hooks', () => ({
  useFeatures: (selector: (state: Record<string, unknown>) => unknown) => mockUseFeatures(selector),
}))

vi.mock('@/context/provider-context', () => ({
  useProviderContext: () => mockUseProviderContext(),
}))

vi.mock('@/app/components/workflow/store/workflow/use-nodes', () => ({
  default: () => mockUseNodes(),
}))

vi.mock('reactflow', () => ({
  useEdges: () => mockUseEdges(),
}))

vi.mock('@/app/components/app/app-publisher', () => ({
  AppPublisher: (props: AppPublisherProps) => {
    const inputs = props.inputs ?? []
    return (
      <div
        data-testid="app-publisher"
        data-disabled={String(Boolean(props.disabled))}
        data-publish-disabled={String(Boolean(props.publishDisabled))}
        data-start-node-limit-exceeded={String(Boolean(props.startNodeLimitExceeded))}
        data-has-trigger-node={String(Boolean(props.hasTriggerNode))}
        data-inputs={JSON.stringify(inputs)}
      >
        <button
          type="button"
          onClick={() => {
            props.onRefreshData?.()
          }}
        >
          publisher-refresh
        </button>
        <button
          type="button"
          onClick={() => {
            props.onToggle?.(true)
          }}
        >
          publisher-toggle-on
        </button>
        <button
          type="button"
          onClick={() => {
            props.onToggle?.(false)
          }}
        >
          publisher-toggle-off
        </button>
        <button
          type="button"
          onClick={() => {
            Promise.resolve(props.onPublish?.()).catch(() => undefined)
          }}
        >
          publisher-publish
        </button>
        <button
          type="button"
          onClick={() => {
            Promise.resolve(
              props.onPublish?.({
                url: '/apps/app-1/workflows/publish',
                title: 'Test title',
                releaseNotes: 'Test notes',
              }),
            ).catch(() => undefined)
          }}
        >
          publisher-publish-with-params
        </button>
        <button
          type="button"
          onClick={() => {
            Promise.resolve(
              props.onPublish?.({
                url: '/apps/app-id/workflows/publish/custom',
                title: 'Custom title',
                releaseNotes: 'Custom notes',
              }),
            ).catch(() => undefined)
          }}
        >
          publisher-publish-custom
        </button>
      </div>
    )
  },
}))

vi.mock('@/service/use-workflow', () => ({
  useInvalidateAppWorkflow: () => mockUpdatePublishedWorkflow,
  usePublishWorkflow: () => ({ mutateAsync: mockPublishWorkflow }),
  useResetWorkflowVersionHistory: () => mockResetWorkflowVersionHistory,
}))

vi.mock('@/service/use-tools', () => ({
  useInvalidateAppTriggers: () => mockInvalidateAppTriggers,
}))

vi.mock('@/service/apps', () => ({
  fetchAppDetail: (...args: unknown[]) => mockFetchAppDetail(...args),
}))

vi.mock('@/hooks/use-theme', () => ({
  default: () => mockUseTheme(),
}))

// Use real app store - global zustand mock will auto-reset between tests

const createProviderContext = ({
  type = Plan.sandbox,
  isFetchedPlan = true,
}: {
  type?: Plan
  isFetchedPlan?: boolean
}) => ({
  plan: { type },
  isFetchedPlan,
})

const renderWithToast = (ui: ReactElement) => {
  const queryClient = new QueryClient()
  return {
    queryClient,
    ...render(<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>),
  }
}

describe('FeaturesTrigger', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    workflowStoreState = {
      showFeaturesPanel: false,
      isRestoring: false,
      setShowFeaturesPanel: mockWorkflowStoreSetShowFeaturesPanel,
      setPublishedAt: mockSetPublishedAt,
      setLastPublishedHasUserInput: mockSetLastPublishedHasUserInput,
    }

    mockUseTheme.mockReturnValue({ theme: 'light' })
    mockUseNodesReadOnly.mockReturnValue({ nodesReadOnly: false, getNodesReadOnly: () => false })
    mockUseChecklist.mockReturnValue([])
    mockUseChecklistBeforePublish.mockReturnValue({
      handleCheckBeforePublish: mockHandleCheckBeforePublish,
    })
    mockHandleCheckBeforePublish.mockResolvedValue(true)
    mockUseNodesSyncDraft.mockReturnValue({ handleSyncWorkflowDraft: mockHandleSyncWorkflowDraft })
    mockHandleSyncWorkflowDraft.mockResolvedValue({ hash: 'draft-hash', updatedAt: 1 })
    mockUseFeatures.mockImplementation((selector: (state: Record<string, unknown>) => unknown) =>
      selector({ features: { file: {} } }),
    )
    mockUseProviderContext.mockReturnValue(createProviderContext({}))
    mockUseNodes.mockReturnValue([])
    mockUseEdges.mockReturnValue([])
    // Set up app store state
    useAppStore.setState({ appDetail: { id: 'app-id' } as unknown as App })
    mockFetchAppDetail.mockResolvedValue({ id: 'app-id', name: 'Updated App' })
    mockInvalidateQueries.mockResolvedValue(undefined)
    mockPublishWorkflow.mockResolvedValue({ created_at: '2024-01-01T00:00:00Z' })
  })

  // Verifies the feature toggle button only appears in chatflow mode.
  describe('Rendering', () => {
    it('should not render the features button when not in chat mode', () => {
      // Arrange
      mockUseIsChatMode.mockReturnValue(false)

      // Act
      renderWithToast(<FeaturesTrigger />)

      // Assert
      expect(
        screen.queryByRole('button', { name: /workflow\.common\.features/i }),
      ).not.toBeInTheDocument()
    })

    it('should render the features button when in chat mode', () => {
      // Arrange
      mockUseIsChatMode.mockReturnValue(true)

      // Act
      renderWithToast(<FeaturesTrigger />)

      // Assert
      expect(
        screen.getByRole('button', { name: /workflow\.common\.features/i }),
      ).toBeInTheDocument()
    })

    it('should apply dark theme styling when theme is dark', () => {
      // Arrange
      mockUseIsChatMode.mockReturnValue(true)
      mockUseTheme.mockReturnValue({ theme: 'dark' })

      // Act
      renderWithToast(<FeaturesTrigger />)

      // Assert
      expect(screen.getByRole('button', { name: /workflow\.common\.features/i })).toHaveClass(
        'rounded-lg',
      )
    })
  })

  // Verifies user clicks toggle the features panel visibility.
  describe('User Interactions', () => {
    it('should toggle features panel when clicked and nodes are editable', async () => {
      // Arrange
      const user = userEvent.setup()
      mockUseIsChatMode.mockReturnValue(true)
      mockUseNodesReadOnly.mockReturnValue({ nodesReadOnly: false, getNodesReadOnly: () => false })

      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: /workflow\.common\.features/i }))

      // Assert
      expect(mockWorkflowStoreSetShowFeaturesPanel).toHaveBeenCalledWith(true)
    })
  })

  // Covers read-only gating that prevents toggling unless restoring.
  describe('Edge Cases', () => {
    it('should not toggle features panel when nodes are read-only and not restoring', async () => {
      // Arrange
      const user = userEvent.setup()
      mockUseIsChatMode.mockReturnValue(true)
      mockUseNodesReadOnly.mockReturnValue({ nodesReadOnly: true, getNodesReadOnly: () => true })
      workflowStoreState = {
        ...workflowStoreState,
        isRestoring: false,
      }

      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: /workflow\.common\.features/i }))

      // Assert
      expect(mockWorkflowStoreSetShowFeaturesPanel).not.toHaveBeenCalled()
    })
  })

  // Verifies the publisher reflects the presence of workflow nodes.
  describe('Props', () => {
    it('should disable AppPublisher when there are no workflow nodes', () => {
      // Arrange
      mockUseIsChatMode.mockReturnValue(false)
      mockUseNodes.mockReturnValue([])

      // Act
      renderWithToast(<FeaturesTrigger />)

      // Assert
      expect(screen.getByTestId('app-publisher')).toHaveAttribute('data-disabled', 'true')
    })
  })

  // Verifies derived props passed into AppPublisher (variables, limits, and triggers).
  describe('Computed Props', () => {
    it('should append image input when file image upload is enabled', () => {
      // Arrange
      mockUseFeatures.mockImplementation((selector: (state: Record<string, unknown>) => unknown) =>
        selector({
          features: { file: { image: { enabled: true } } },
        }),
      )
      mockUseNodes.mockReturnValue([{ id: 'start', data: { type: BlockEnum.Start } }])

      // Act
      renderWithToast(<FeaturesTrigger />)

      // Assert
      const inputs = JSON.parse(
        screen.getByTestId('app-publisher').getAttribute('data-inputs') ?? '[]',
      ) as Array<{
        type?: string
        variable?: string
        required?: boolean
        label?: string
      }>
      expect(inputs).toContainEqual({
        type: InputVarType.files,
        variable: '__image',
        required: false,
        label: 'files',
      })
    })

    it('should set startNodeLimitExceeded when sandbox entry limit is exceeded', () => {
      // Arrange
      mockUseNodes.mockReturnValue([
        { id: 'start', data: { type: BlockEnum.Start } },
        { id: 'trigger-1', data: { type: BlockEnum.TriggerWebhook } },
        { id: 'trigger-2', data: { type: BlockEnum.TriggerSchedule } },
        { id: 'end', data: { type: BlockEnum.End } },
      ])

      // Act
      renderWithToast(<FeaturesTrigger />)

      // Assert
      const publisher = screen.getByTestId('app-publisher')
      expect(publisher).toHaveAttribute('data-start-node-limit-exceeded', 'true')
      expect(publisher).toHaveAttribute('data-publish-disabled', 'true')
      expect(publisher).toHaveAttribute('data-has-trigger-node', 'true')
    })
  })

  // Verifies callbacks wired from AppPublisher to stores and draft syncing.
  describe('Callbacks', () => {
    it('should set toolPublished when AppPublisher refreshes data', async () => {
      // Arrange
      const user = userEvent.setup()
      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-refresh' }))

      // Assert
      expect(mockWorkflowStoreSetState).toHaveBeenCalledWith({ toolPublished: true })
    })

    it('should sync workflow draft when AppPublisher toggles on', async () => {
      // Arrange
      const user = userEvent.setup()
      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-toggle-on' }))

      // Assert
      expect(mockHandleSyncWorkflowDraft).toHaveBeenCalledWith(true)
    })

    it('should not sync workflow draft when AppPublisher toggles off', async () => {
      // Arrange
      const user = userEvent.setup()
      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-toggle-off' }))

      // Assert
      expect(mockHandleSyncWorkflowDraft).not.toHaveBeenCalled()
    })
  })

  // Verifies publishing behavior across warnings, validation, and success.
  describe('Publishing', () => {
    it('should wait for the draft save barrier before publishing', async () => {
      const user = userEvent.setup()
      let resolveDraftSync: ((value: { hash: string; updatedAt: number }) => void) | undefined
      mockHandleSyncWorkflowDraft.mockReturnValueOnce(
        new Promise((resolve) => {
          resolveDraftSync = resolve
        }),
      )
      renderWithToast(<FeaturesTrigger />)

      await user.click(screen.getByRole('button', { name: 'publisher-publish' }))

      await waitFor(() => {
        expect(mockHandleSyncWorkflowDraft).toHaveBeenCalledWith(true)
      })
      expect(mockPublishWorkflow).not.toHaveBeenCalled()

      resolveDraftSync?.({ hash: 'saved-hash', updatedAt: 2 })

      await waitFor(() => {
        expect(mockPublishWorkflow).toHaveBeenCalled()
      })
    })

    it('should not publish when the draft save barrier fails', async () => {
      const user = userEvent.setup()
      mockHandleSyncWorkflowDraft.mockResolvedValueOnce(null)
      renderWithToast(<FeaturesTrigger />)

      await user.click(screen.getByRole('button', { name: 'publisher-publish' }))

      await waitFor(() => {
        expect(mockHandleSyncWorkflowDraft).toHaveBeenCalledWith(true)
      })
      expect(mockPublishWorkflow).not.toHaveBeenCalled()
    })

    it('should notify error and reject publish when checklist has warning nodes', async () => {
      // Arrange
      const user = userEvent.setup()
      mockUseChecklist.mockReturnValue([{ id: 'warning' }])
      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-publish' }))

      // Assert
      await waitFor(() => {
        expect(toastMocks.call).toHaveBeenCalledWith({
          type: 'error',
          message: 'workflow.panel.checklistTip',
        })
      })
      expect(mockPublishWorkflow).not.toHaveBeenCalled()
    })

    it('should reject publish when checklist before publish fails', async () => {
      // Arrange
      const user = userEvent.setup()
      mockHandleCheckBeforePublish.mockResolvedValue(false)
      renderWithToast(<FeaturesTrigger />)

      // Act & Assert
      await user.click(screen.getByRole('button', { name: 'publisher-publish' }))

      await waitFor(() => {
        expect(mockHandleCheckBeforePublish).toHaveBeenCalled()
      })
      expect(mockPublishWorkflow).not.toHaveBeenCalled()
    })

    it('should publish workflow and update related stores when validation passes', async () => {
      // Arrange
      const user = userEvent.setup()
      mockUseNodes.mockReturnValue([{ id: 'start', data: { type: BlockEnum.Start } }])
      mockUseEdges.mockReturnValue([{ source: 'start' }])
      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-publish' }))

      // Assert
      await waitFor(() => {
        expect(mockPublishWorkflow).toHaveBeenCalledWith({
          url: '/apps/app-id/workflows/publish',
          title: '',
          releaseNotes: '',
        })
        expect(mockUpdatePublishedWorkflow).toHaveBeenCalledWith('app-id')
        expect(mockInvalidateAppTriggers).toHaveBeenCalledWith('app-id')
        expect(mockSetPublishedAt).toHaveBeenCalledWith('2024-01-01T00:00:00Z')
        expect(mockSetLastPublishedHasUserInput).toHaveBeenCalledWith(true)
        expect(mockResetWorkflowVersionHistory).toHaveBeenCalled()
        expect(toastMocks.call).toHaveBeenCalledWith({
          type: 'success',
          message: 'common.api.actionSuccess',
        })
        expect(mockFetchAppDetail).toHaveBeenCalledWith({ url: '/apps', id: 'app-id' })
        expect(mockSetQueryData).toHaveBeenCalledWith(
          ['apps', 'detail', 'app-id'],
          expect.objectContaining({
            name: 'Updated App',
          }),
        )
        expect(useAppStore.getState().appDetail).toEqual(
          expect.objectContaining({
            name: 'Updated App',
          }),
        )
      })
    })

    it('should invalidate roster list after publishing a workflow with a roster Agent v2 node', async () => {
      // Arrange
      const user = userEvent.setup()
      mockUseNodes.mockReturnValue([
        { id: 'start', data: { type: BlockEnum.Start } },
        {
          id: 'agent-v2',
          data: {
            type: BlockEnum.AgentV2,
            version: '2',
            agent_node_kind: 'dify_agent',
            agent_binding: {
              binding_type: 'roster_agent',
              agent_id: 'agent-1',
            },
          },
        },
      ])
      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-publish' }))

      // Assert
      await waitFor(() => {
        expect(mockInvalidateQueries).toHaveBeenCalledWith({
          queryKey: consoleQuery.agent.get.key(),
        })
        expect(mockInvalidateQueries).toHaveBeenCalledWith({
          queryKey: consoleQuery.agent.byAgentId.referencingWorkflows.get.queryOptions({
            input: {
              params: {
                agent_id: 'agent-1',
              },
            },
          }).queryKey,
        })
      })
    })

    it('should keep roster list cache stable after publishing a workflow without roster Agent v2 nodes', async () => {
      // Arrange
      const user = userEvent.setup()
      mockUseNodes.mockReturnValue([
        { id: 'start', data: { type: BlockEnum.Start } },
        {
          id: 'inline-agent-v2',
          data: {
            type: BlockEnum.AgentV2,
            version: '2',
            agent_node_kind: 'dify_agent',
            agent_binding: {
              binding_type: 'inline_agent',
              agent_id: 'agent-1',
              current_snapshot_id: 'snapshot-1',
            },
          },
        },
      ])
      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-publish' }))

      // Assert
      await waitFor(() => {
        expect(mockPublishWorkflow).toHaveBeenCalled()
      })
      expect(mockInvalidateQueries).not.toHaveBeenCalledWith({
        queryKey: consoleQuery.agent.get.key(),
      })
      expect(mockInvalidateQueries).not.toHaveBeenCalledWith({
        queryKey: consoleQuery.agent.byAgentId.referencingWorkflows.get.queryOptions({
          input: {
            params: {
              agent_id: 'agent-1',
            },
          },
        }).queryKey,
      })
    })

    it('should pass publish params to workflow publish mutation', async () => {
      // Arrange
      const user = userEvent.setup()
      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-publish-with-params' }))

      // Assert
      await waitFor(() => {
        expect(mockPublishWorkflow).toHaveBeenCalledWith({
          url: '/apps/app-1/workflows/publish',
          title: 'Test title',
          releaseNotes: 'Test notes',
        })
      })
    })

    it('should respect the publish url passed by the publisher', async () => {
      // Arrange
      const user = userEvent.setup()
      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-publish-custom' }))

      // Assert
      await waitFor(() => {
        expect(mockPublishWorkflow).toHaveBeenCalledWith({
          url: '/apps/app-id/workflows/publish/custom',
          title: 'Custom title',
          releaseNotes: 'Custom notes',
        })
      })
    })

    it('should skip success side effects when publish mutation returns no workflow version', async () => {
      // Arrange
      const user = userEvent.setup()
      mockPublishWorkflow.mockResolvedValue(null)
      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-publish' }))

      // Assert
      await waitFor(() => {
        expect(mockPublishWorkflow).toHaveBeenCalled()
      })
      expect(toastMocks.call).not.toHaveBeenCalledWith({
        type: 'success',
        message: 'common.api.actionSuccess',
      })
      expect(mockUpdatePublishedWorkflow).not.toHaveBeenCalled()
      expect(mockInvalidateAppTriggers).not.toHaveBeenCalled()
      expect(mockSetPublishedAt).not.toHaveBeenCalled()
      expect(mockSetLastPublishedHasUserInput).not.toHaveBeenCalled()
      expect(mockResetWorkflowVersionHistory).not.toHaveBeenCalled()
    })

    it('should log error when app detail refresh fails after publish', async () => {
      // Arrange
      const user = userEvent.setup()
      const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => undefined)
      mockFetchAppDetail.mockRejectedValueOnce(new Error('fetch failed'))

      renderWithToast(<FeaturesTrigger />)

      // Act
      await user.click(screen.getByRole('button', { name: 'publisher-publish' }))

      // Assert
      await waitFor(() => {
        expect(consoleErrorSpy).toHaveBeenCalled()
      })
      consoleErrorSpy.mockRestore()
    })
  })
})
