mirror of
https://mirror.skon.top/github.com/langgenius/dify.git
synced 2026-04-22 01:11:02 +08:00
refactor(web): complete migration from PortalToFollowElem to Popover component in tests
This commit finalizes the transition from the PortalToFollowElem to the Popover component across various test files. The updates include the implementation of Popover's context and trigger handling, ensuring consistent behavior in the UI. All relevant tests have been adjusted to reflect these changes, enhancing the overall test coverage and reliability.
This commit is contained in:
@@ -10,6 +10,72 @@ vi.mock('@/next/navigation', () => ({
|
||||
usePathname: () => '/test',
|
||||
}))
|
||||
|
||||
vi.mock('@langgenius/dify-ui/popover', async () => {
|
||||
const React = await import('react')
|
||||
const PopoverContext = React.createContext({
|
||||
open: false,
|
||||
setOpen: (_open: boolean) => {},
|
||||
})
|
||||
|
||||
const Popover = ({
|
||||
children,
|
||||
open: controlledOpen,
|
||||
onOpenChange,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}) => {
|
||||
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false)
|
||||
const isControlled = controlledOpen !== undefined
|
||||
const open = isControlled ? !!controlledOpen : uncontrolledOpen
|
||||
const setOpen = (nextOpen: boolean) => {
|
||||
if (!isControlled)
|
||||
setUncontrolledOpen(nextOpen)
|
||||
onOpenChange?.(nextOpen)
|
||||
}
|
||||
|
||||
return (
|
||||
<PopoverContext.Provider value={{ open, setOpen }}>
|
||||
{children}
|
||||
</PopoverContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
const PopoverTrigger = ({ render }: { render: React.ReactElement }) => {
|
||||
const { open, setOpen } = React.useContext(PopoverContext)
|
||||
return React.cloneElement(render, {
|
||||
'data-testid': 'popover-trigger',
|
||||
'onClick': (e: React.MouseEvent<HTMLElement>) => {
|
||||
render.props.onClick?.(e)
|
||||
if (!e.defaultPrevented)
|
||||
setOpen(!open)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const PopoverContent = ({
|
||||
children,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement> & { children?: React.ReactNode }) => {
|
||||
const { open } = React.useContext(PopoverContext)
|
||||
if (!open)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div data-testid="popover-content" {...props}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
}
|
||||
})
|
||||
|
||||
type PortalToFollowElemProps = {
|
||||
children: React.ReactNode
|
||||
open?: boolean
|
||||
@@ -209,20 +275,17 @@ describe('ContextVar', () => {
|
||||
// Act
|
||||
render(<ContextVar {...props} />)
|
||||
|
||||
const triggers = screen.getAllByTestId('portal-trigger')
|
||||
const varPickerTrigger = triggers[triggers.length - 1]
|
||||
const varPickerTrigger = screen.getByTestId('popover-trigger')
|
||||
|
||||
await user.click(varPickerTrigger!)
|
||||
expect(screen.getByTestId('portal-content'))!.toBeInTheDocument()
|
||||
expect(screen.getByTestId('popover-content'))!.toBeInTheDocument()
|
||||
|
||||
// Select a different option
|
||||
const options = screen.getAllByText('var2')
|
||||
expect(options.length).toBeGreaterThan(0)
|
||||
await user.click(options[0]!)
|
||||
await user.click(screen.getByText('var2'))
|
||||
|
||||
// Assert
|
||||
expect(onChange).toHaveBeenCalledWith('var2')
|
||||
expect(screen.queryByTestId('portal-content')).not.toBeInTheDocument()
|
||||
expect(screen.queryByTestId('popover-content')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should toggle dropdown when clicking the trigger button', async () => {
|
||||
@@ -233,16 +296,15 @@ describe('ContextVar', () => {
|
||||
// Act
|
||||
render(<ContextVar {...props} />)
|
||||
|
||||
const triggers = screen.getAllByTestId('portal-trigger')
|
||||
const varPickerTrigger = triggers[triggers.length - 1]
|
||||
const varPickerTrigger = screen.getByTestId('popover-trigger')
|
||||
|
||||
// Open dropdown
|
||||
await user.click(varPickerTrigger!)
|
||||
expect(screen.getByTestId('portal-content'))!.toBeInTheDocument()
|
||||
expect(screen.getByTestId('popover-content'))!.toBeInTheDocument()
|
||||
|
||||
// Close dropdown
|
||||
await user.click(varPickerTrigger!)
|
||||
expect(screen.queryByTestId('portal-content')).not.toBeInTheDocument()
|
||||
expect(screen.queryByTestId('popover-content')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -4,6 +4,61 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { TriggerCredentialTypeEnum } from '@/app/components/workflow/block-selector/types'
|
||||
import { SubscriptionSelectorEntry } from '../selector-entry'
|
||||
|
||||
vi.mock('@langgenius/dify-ui/popover', async () => {
|
||||
const React = await import('react')
|
||||
const PopoverContext = React.createContext({
|
||||
open: false,
|
||||
setOpen: (_open: boolean) => {},
|
||||
})
|
||||
|
||||
const Popover = ({
|
||||
children,
|
||||
open: controlledOpen,
|
||||
onOpenChange,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}) => {
|
||||
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false)
|
||||
const isControlled = controlledOpen !== undefined
|
||||
const open = isControlled ? !!controlledOpen : uncontrolledOpen
|
||||
const setOpen = (nextOpen: boolean) => {
|
||||
if (!isControlled)
|
||||
setUncontrolledOpen(nextOpen)
|
||||
onOpenChange?.(nextOpen)
|
||||
}
|
||||
|
||||
return (
|
||||
<PopoverContext.Provider value={{ open, setOpen }}>
|
||||
{children}
|
||||
</PopoverContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
const PopoverTrigger = ({ render }: { render: React.ReactElement }) => {
|
||||
const { open, setOpen } = React.useContext(PopoverContext)
|
||||
return React.cloneElement(render, {
|
||||
onClick: (e: React.MouseEvent<HTMLElement>) => {
|
||||
render.props.onClick?.(e)
|
||||
if (!e.defaultPrevented)
|
||||
setOpen(!open)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const PopoverContent = ({ children }: { children: React.ReactNode }) => {
|
||||
const { open } = React.useContext(PopoverContext)
|
||||
return open ? <div data-testid="popover-content">{children}</div> : null
|
||||
}
|
||||
|
||||
return {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
}
|
||||
})
|
||||
|
||||
let mockSubscriptions: TriggerSubscription[] = []
|
||||
const mockRefetch = vi.fn()
|
||||
|
||||
@@ -92,6 +147,6 @@ describe('SubscriptionSelectorEntry', () => {
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Subscription One' }))
|
||||
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ id: 'sub-1', name: 'Subscription One' }), expect.any(Function))
|
||||
expect(screen.queryByText('Subscription One')).not.toBeInTheDocument()
|
||||
expect(screen.queryByTestId('popover-content')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,8 +4,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { PluginSource } from '@/app/components/plugins/types'
|
||||
import ToolPicker from '../tool-picker'
|
||||
|
||||
let portalOpen = false
|
||||
|
||||
const mockInstalledPluginList = vi.hoisted(() => ({
|
||||
data: {
|
||||
plugins: [] as PluginDetail[],
|
||||
@@ -21,33 +19,53 @@ vi.mock('@/app/components/base/loading', () => ({
|
||||
default: () => <div data-testid="loading">loading</div>,
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/portal-to-follow-elem', async () => {
|
||||
const _React = await import('react')
|
||||
vi.mock('@langgenius/dify-ui/popover', async () => {
|
||||
const React = await import('react')
|
||||
const PopoverContext = React.createContext({
|
||||
open: false,
|
||||
setOpen: (_open: boolean) => {},
|
||||
})
|
||||
|
||||
const Popover = ({
|
||||
children,
|
||||
open,
|
||||
onOpenChange,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}) => (
|
||||
<PopoverContext.Provider value={{ open: !!open, setOpen: (nextOpen: boolean) => onOpenChange?.(nextOpen) }}>
|
||||
{children}
|
||||
</PopoverContext.Provider>
|
||||
)
|
||||
|
||||
const PopoverTrigger = ({ render }: { render: React.ReactElement }) => {
|
||||
const { open, setOpen } = React.useContext(PopoverContext)
|
||||
return React.cloneElement(render, {
|
||||
onClick: (e: React.MouseEvent<HTMLElement>) => {
|
||||
render.props.onClick?.(e)
|
||||
if (!e.defaultPrevented)
|
||||
setOpen(!open)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const PopoverContent = ({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}) => {
|
||||
const { open } = React.useContext(PopoverContext)
|
||||
return open ? <div data-testid="popover-content" className={className}>{children}</div> : null
|
||||
}
|
||||
|
||||
return {
|
||||
PortalToFollowElem: ({
|
||||
open,
|
||||
children,
|
||||
}: {
|
||||
open: boolean
|
||||
children: React.ReactNode
|
||||
}) => {
|
||||
portalOpen = open
|
||||
return <div>{children}</div>
|
||||
},
|
||||
PortalToFollowElemTrigger: ({
|
||||
children,
|
||||
onClick,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
onClick: () => void
|
||||
}) => <button data-testid="trigger" onClick={onClick}>{children}</button>,
|
||||
PortalToFollowElemContent: ({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
}) => portalOpen ? <div data-testid="portal-content" className={className}>{children}</div> : null,
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -118,7 +136,6 @@ const createPlugin = (
|
||||
describe('ToolPicker', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
portalOpen = false
|
||||
mockInstalledPluginList.data = {
|
||||
plugins: [],
|
||||
}
|
||||
@@ -137,7 +154,7 @@ describe('ToolPicker', () => {
|
||||
/>,
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByTestId('trigger'))
|
||||
fireEvent.click(screen.getByText('trigger'))
|
||||
|
||||
expect(onShowChange).toHaveBeenCalledWith(true)
|
||||
})
|
||||
|
||||
@@ -2,6 +2,67 @@ import { act, fireEvent, render, screen } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import LabelSelector from '../selector'
|
||||
|
||||
vi.mock('@langgenius/dify-ui/popover', async () => {
|
||||
const React = await import('react')
|
||||
const PopoverContext = React.createContext({
|
||||
open: false,
|
||||
setOpen: (_open: boolean) => {},
|
||||
})
|
||||
|
||||
const Popover = ({
|
||||
children,
|
||||
open: controlledOpen,
|
||||
onOpenChange,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}) => {
|
||||
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false)
|
||||
const isControlled = controlledOpen !== undefined
|
||||
const open = isControlled ? !!controlledOpen : uncontrolledOpen
|
||||
const setOpen = (nextOpen: boolean) => {
|
||||
if (!isControlled)
|
||||
setUncontrolledOpen(nextOpen)
|
||||
onOpenChange?.(nextOpen)
|
||||
}
|
||||
|
||||
return (
|
||||
<PopoverContext.Provider value={{ open, setOpen }}>
|
||||
{children}
|
||||
</PopoverContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
const PopoverTrigger = ({ render }: { render: React.ReactElement }) => {
|
||||
const { open, setOpen } = React.useContext(PopoverContext)
|
||||
return React.cloneElement(render, {
|
||||
onClick: (e: React.MouseEvent<HTMLElement>) => {
|
||||
render.props.onClick?.(e)
|
||||
if (!e.defaultPrevented)
|
||||
setOpen(!open)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const PopoverContent = ({
|
||||
children,
|
||||
...props
|
||||
}: React.HTMLAttributes<HTMLDivElement> & { children?: React.ReactNode }) => {
|
||||
const { open } = React.useContext(PopoverContext)
|
||||
if (!open)
|
||||
return null
|
||||
|
||||
return <div {...props}>{children}</div>
|
||||
}
|
||||
|
||||
return {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
}
|
||||
})
|
||||
|
||||
// Mock useTags hook with controlled test data
|
||||
const mockTags = [
|
||||
{ name: 'agent', label: 'Agent' },
|
||||
|
||||
@@ -2,6 +2,61 @@ import type { Member } from '@/models/common'
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import MemberSelector from '../member-selector'
|
||||
|
||||
vi.mock('@langgenius/dify-ui/popover', async () => {
|
||||
const React = await import('react')
|
||||
const PopoverContext = React.createContext({
|
||||
open: false,
|
||||
setOpen: (_open: boolean) => {},
|
||||
})
|
||||
|
||||
const Popover = ({
|
||||
children,
|
||||
open: controlledOpen,
|
||||
onOpenChange,
|
||||
}: {
|
||||
children: import('react').ReactNode
|
||||
open?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
}) => {
|
||||
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(false)
|
||||
const isControlled = controlledOpen !== undefined
|
||||
const open = isControlled ? !!controlledOpen : uncontrolledOpen
|
||||
const setOpen = (nextOpen: boolean) => {
|
||||
if (!isControlled)
|
||||
setUncontrolledOpen(nextOpen)
|
||||
onOpenChange?.(nextOpen)
|
||||
}
|
||||
|
||||
return (
|
||||
<PopoverContext.Provider value={{ open, setOpen }}>
|
||||
{children}
|
||||
</PopoverContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
const PopoverTrigger = ({ render }: { render: import('react').ReactElement }) => {
|
||||
const { open, setOpen } = React.useContext(PopoverContext)
|
||||
return React.cloneElement(render, {
|
||||
onClick: (e: import('react').MouseEvent<HTMLElement>) => {
|
||||
render.props.onClick?.(e)
|
||||
if (!e.defaultPrevented)
|
||||
setOpen(!open)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const PopoverContent = ({ children }: { children: import('react').ReactNode }) => {
|
||||
const { open } = React.useContext(PopoverContext)
|
||||
return open ? <div data-testid="popover-content">{children}</div> : null
|
||||
}
|
||||
|
||||
return {
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
}
|
||||
})
|
||||
|
||||
const mockMemberList = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock('../member-list', () => ({
|
||||
|
||||
Reference in New Issue
Block a user