mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 17:08:03 +08:00
Merge remote-tracking branch 'origin/main' into feat/model-plugins-implementing
This commit is contained in:
@ -3,12 +3,10 @@ import { Avatar } from '../index'
|
||||
|
||||
describe('Avatar', () => {
|
||||
describe('Rendering', () => {
|
||||
it('should render img element when avatar URL is provided', () => {
|
||||
it('should keep the fallback visible when avatar URL is provided before image load', () => {
|
||||
render(<Avatar name="John Doe" avatar="https://example.com/avatar.jpg" />)
|
||||
|
||||
const img = screen.getByRole('img', { name: 'John Doe' })
|
||||
expect(img).toBeInTheDocument()
|
||||
expect(img).toHaveAttribute('src', 'https://example.com/avatar.jpg')
|
||||
expect(screen.getByText('J')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render fallback with uppercase initial when avatar is null', () => {
|
||||
@ -18,10 +16,9 @@ describe('Avatar', () => {
|
||||
expect(screen.getByText('A')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render both image and fallback when avatar is provided', () => {
|
||||
it('should render the fallback when avatar is provided', () => {
|
||||
render(<Avatar name="John" avatar="https://example.com/avatar.jpg" />)
|
||||
|
||||
expect(screen.getByRole('img')).toBeInTheDocument()
|
||||
expect(screen.getByText('J')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@ -90,7 +87,7 @@ describe('Avatar', () => {
|
||||
})
|
||||
|
||||
describe('onLoadingStatusChange', () => {
|
||||
it('should render image when avatar and onLoadingStatusChange are provided', () => {
|
||||
it('should render the fallback when avatar and onLoadingStatusChange are provided', () => {
|
||||
render(
|
||||
<Avatar
|
||||
name="John"
|
||||
@ -99,7 +96,7 @@ describe('Avatar', () => {
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByRole('img')).toBeInTheDocument()
|
||||
expect(screen.getByText('J')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not render image when avatar is null even with onLoadingStatusChange', () => {
|
||||
|
||||
@ -978,7 +978,7 @@ describe('ChatWrapper', () => {
|
||||
expect(screen.getByAltText('answer icon')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should render question icon when user avatar is available', () => {
|
||||
it('should render question icon fallback when user avatar is available', () => {
|
||||
vi.mocked(useChatWithHistoryContext).mockReturnValue({
|
||||
...defaultContextValue,
|
||||
initUserVariables: {
|
||||
@ -992,12 +992,11 @@ describe('ChatWrapper', () => {
|
||||
chatList: [{ id: 'q1', content: 'Question' }],
|
||||
} as unknown as ChatHookReturn)
|
||||
|
||||
const { container } = render(<ChatWrapper />)
|
||||
const avatar = container.querySelector('img[alt="John Doe"]')
|
||||
expect(avatar).toBeInTheDocument()
|
||||
render(<ChatWrapper />)
|
||||
expect(screen.getByText('J')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should use fallback values for nullable appData, appMeta and user name', () => {
|
||||
it('should use fallback values for nullable appData, appMeta and avatar name', () => {
|
||||
vi.mocked(useChatWithHistoryContext).mockReturnValue({
|
||||
...defaultContextValue,
|
||||
appData: null as unknown as AppData,
|
||||
@ -1014,7 +1013,7 @@ describe('ChatWrapper', () => {
|
||||
|
||||
render(<ChatWrapper />)
|
||||
expect(screen.getByText('Question with fallback avatar name')).toBeInTheDocument()
|
||||
expect(screen.getByAltText('user')).toBeInTheDocument()
|
||||
expect(screen.getByText('U')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should set handleStop on currentChatInstanceRef', () => {
|
||||
|
||||
@ -327,7 +327,7 @@ describe('EmbeddedChatbot chat-wrapper', () => {
|
||||
expect(screen.getByRole('button', { name: 'send message' })).toBeDisabled()
|
||||
})
|
||||
|
||||
it('should show the user name when avatar data is provided', () => {
|
||||
it('should show the user avatar fallback when avatar data is provided', () => {
|
||||
vi.mocked(useEmbeddedChatbotContext).mockReturnValue(createContextValue({
|
||||
initUserVariables: {
|
||||
avatar_url: 'https://example.com/avatar.png',
|
||||
@ -337,7 +337,7 @@ describe('EmbeddedChatbot chat-wrapper', () => {
|
||||
|
||||
render(<ChatWrapper />)
|
||||
|
||||
expect(screen.getByRole('img', { name: 'Alice' })).toBeInTheDocument()
|
||||
expect(screen.getByText('A')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -639,128 +639,50 @@ describe('Mermaid Flowchart Component Module Isolation', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('should tolerate missing hidden container during classic render and cleanup', async () => {
|
||||
vi.resetModules()
|
||||
let pendingContainerRef: unknown | null = null
|
||||
let patchedContainerRef = false
|
||||
let patchedTimeoutRef = false
|
||||
let containerReadCount = 0
|
||||
const virtualContainer = { innerHTML: 'seed' } as HTMLDivElement
|
||||
|
||||
vi.doMock('react', async () => {
|
||||
const reactActual = await vi.importActual<typeof import('react')>('react')
|
||||
const mockedUseRef = ((initialValue: unknown) => {
|
||||
const ref = reactActual.useRef(initialValue as never)
|
||||
if (!patchedContainerRef && initialValue === null)
|
||||
pendingContainerRef = ref
|
||||
|
||||
if (!patchedContainerRef
|
||||
&& pendingContainerRef
|
||||
&& typeof initialValue === 'string'
|
||||
&& initialValue.startsWith('mermaid-chart-')) {
|
||||
Object.defineProperty(pendingContainerRef as { current: unknown }, 'current', {
|
||||
configurable: true,
|
||||
get() {
|
||||
containerReadCount += 1
|
||||
if (containerReadCount === 1)
|
||||
return virtualContainer
|
||||
return null
|
||||
},
|
||||
set(_value: HTMLDivElement | null) { },
|
||||
})
|
||||
patchedContainerRef = true
|
||||
pendingContainerRef = null
|
||||
}
|
||||
|
||||
if (patchedContainerRef && !patchedTimeoutRef && initialValue === undefined) {
|
||||
patchedTimeoutRef = true
|
||||
Object.defineProperty(ref, 'current', {
|
||||
configurable: true,
|
||||
get() {
|
||||
return undefined
|
||||
},
|
||||
set(_value: NodeJS.Timeout | undefined) { },
|
||||
})
|
||||
return ref
|
||||
}
|
||||
|
||||
return ref
|
||||
}) as typeof reactActual.useRef
|
||||
|
||||
return {
|
||||
...reactActual,
|
||||
useRef: mockedUseRef,
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
const { default: FlowchartFresh } = await import('../index')
|
||||
const { unmount } = render(<FlowchartFresh PrimitiveCode={mockCode} />)
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('test-svg')).toBeInTheDocument()
|
||||
}, { timeout: 3000 })
|
||||
unmount()
|
||||
}
|
||||
finally {
|
||||
vi.doUnmock('react')
|
||||
}
|
||||
})
|
||||
|
||||
it('should tolerate missing hidden container during handDrawn render', async () => {
|
||||
vi.resetModules()
|
||||
let pendingContainerRef: unknown | null = null
|
||||
let patchedContainerRef = false
|
||||
let containerReadCount = 0
|
||||
const virtualContainer = { innerHTML: 'seed' } as HTMLDivElement
|
||||
|
||||
vi.doMock('react', async () => {
|
||||
const reactActual = await vi.importActual<typeof import('react')>('react')
|
||||
const mockedUseRef = ((initialValue: unknown) => {
|
||||
const ref = reactActual.useRef(initialValue as never)
|
||||
if (!patchedContainerRef && initialValue === null)
|
||||
pendingContainerRef = ref
|
||||
|
||||
if (!patchedContainerRef
|
||||
&& pendingContainerRef
|
||||
&& typeof initialValue === 'string'
|
||||
&& initialValue.startsWith('mermaid-chart-')) {
|
||||
Object.defineProperty(pendingContainerRef as { current: unknown }, 'current', {
|
||||
configurable: true,
|
||||
get() {
|
||||
containerReadCount += 1
|
||||
if (containerReadCount === 1)
|
||||
return virtualContainer
|
||||
return null
|
||||
},
|
||||
set(_value: HTMLDivElement | null) { },
|
||||
})
|
||||
patchedContainerRef = true
|
||||
pendingContainerRef = null
|
||||
}
|
||||
return ref
|
||||
}) as typeof reactActual.useRef
|
||||
|
||||
return {
|
||||
...reactActual,
|
||||
useRef: mockedUseRef,
|
||||
}
|
||||
})
|
||||
it('should cancel a pending classic render on unmount', async () => {
|
||||
const { default: FlowchartFresh } = await import('../index')
|
||||
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
const { default: FlowchartFresh } = await import('../index')
|
||||
const { rerender } = render(<FlowchartFresh PrimitiveCode="graph" />)
|
||||
const { unmount } = render(<FlowchartFresh PrimitiveCode={mockCode} />)
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText(HAND_DRAWN_RE))
|
||||
rerender(<FlowchartFresh PrimitiveCode={mockCode} />)
|
||||
unmount()
|
||||
await vi.advanceTimersByTimeAsync(350)
|
||||
})
|
||||
await Promise.resolve()
|
||||
expect(screen.getByText('test-svg-api')).toBeInTheDocument()
|
||||
|
||||
expect(vi.mocked(mermaidFresh.render)).not.toHaveBeenCalled()
|
||||
}
|
||||
finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
it('should cancel a pending handDrawn render on unmount', async () => {
|
||||
const { default: FlowchartFresh } = await import('../index')
|
||||
const { unmount } = render(<FlowchartFresh PrimitiveCode={mockCode} />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('test-svg')).toBeInTheDocument()
|
||||
}, { timeout: 3000 })
|
||||
|
||||
const initialHandDrawnCalls = vi.mocked(mermaidFresh.mermaidAPI.render).mock.calls.length
|
||||
|
||||
vi.useFakeTimers()
|
||||
try {
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByText(HAND_DRAWN_RE))
|
||||
})
|
||||
|
||||
await act(async () => {
|
||||
unmount()
|
||||
await vi.advanceTimersByTimeAsync(350)
|
||||
})
|
||||
|
||||
expect(vi.mocked(mermaidFresh.mermaidAPI.render).mock.calls.length).toBe(initialHandDrawnCalls)
|
||||
}
|
||||
finally {
|
||||
vi.useRealTimers()
|
||||
vi.doUnmock('react')
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@ -4,7 +4,6 @@ import { cva } from 'class-variance-authority'
|
||||
import * as React from 'react'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Divider from '../divider'
|
||||
import './index.css'
|
||||
|
||||
type SegmentedControlOption<T> = {
|
||||
value: T
|
||||
@ -131,7 +130,7 @@ export const SegmentedControl = <T extends string | number | symbol>({
|
||||
<div className={cn('inline-flex items-center gap-x-1', ItemTextWrapperVariants({ size }))}>
|
||||
<span>{text}</span>
|
||||
{!!(count && size === 'large') && (
|
||||
<div className="system-2xs-medium-uppercase inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-[5px] text-text-tertiary">
|
||||
<div className="inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-[5px] text-text-tertiary system-2xs-medium-uppercase">
|
||||
{count}
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user