mirror of https://github.com/langgenius/dify.git
chore: some billing test (#29648)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Coding On Star <447357187@qq.com>
This commit is contained in:
parent
a951f46a09
commit
2b3c55d95a
|
|
@ -0,0 +1,71 @@
|
||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import AnnotationFull from './index'
|
||||||
|
|
||||||
|
jest.mock('react-i18next', () => ({
|
||||||
|
useTranslation: () => ({
|
||||||
|
t: (key: string) => key,
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
let mockUsageProps: { className?: string } | null = null
|
||||||
|
jest.mock('./usage', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: (props: { className?: string }) => {
|
||||||
|
mockUsageProps = props
|
||||||
|
return (
|
||||||
|
<div data-testid='usage-component' data-classname={props.className ?? ''}>
|
||||||
|
usage
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
let mockUpgradeBtnProps: { loc?: string } | null = null
|
||||||
|
jest.mock('../upgrade-btn', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: (props: { loc?: string }) => {
|
||||||
|
mockUpgradeBtnProps = props
|
||||||
|
return (
|
||||||
|
<button type='button' data-testid='upgrade-btn'>
|
||||||
|
{props.loc}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('AnnotationFull', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
mockUsageProps = null
|
||||||
|
mockUpgradeBtnProps = null
|
||||||
|
})
|
||||||
|
|
||||||
|
// Rendering marketing copy with action button
|
||||||
|
describe('Rendering', () => {
|
||||||
|
it('should render tips when rendered', () => {
|
||||||
|
// Act
|
||||||
|
render(<AnnotationFull />)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(screen.getByText('billing.annotatedResponse.fullTipLine1')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('billing.annotatedResponse.fullTipLine2')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render upgrade button when rendered', () => {
|
||||||
|
// Act
|
||||||
|
render(<AnnotationFull />)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(screen.getByTestId('upgrade-btn')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should render Usage component when rendered', () => {
|
||||||
|
// Act
|
||||||
|
render(<AnnotationFull />)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const usageComponent = screen.getByTestId('usage-component')
|
||||||
|
expect(usageComponent).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
import { fireEvent, render, screen } from '@testing-library/react'
|
||||||
|
import AnnotationFullModal from './modal'
|
||||||
|
|
||||||
|
jest.mock('react-i18next', () => ({
|
||||||
|
useTranslation: () => ({
|
||||||
|
t: (key: string) => key,
|
||||||
|
}),
|
||||||
|
}))
|
||||||
|
|
||||||
|
let mockUsageProps: { className?: string } | null = null
|
||||||
|
jest.mock('./usage', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: (props: { className?: string }) => {
|
||||||
|
mockUsageProps = props
|
||||||
|
return (
|
||||||
|
<div data-testid='usage-component' data-classname={props.className ?? ''}>
|
||||||
|
usage
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
let mockUpgradeBtnProps: { loc?: string } | null = null
|
||||||
|
jest.mock('../upgrade-btn', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: (props: { loc?: string }) => {
|
||||||
|
mockUpgradeBtnProps = props
|
||||||
|
return (
|
||||||
|
<button type='button' data-testid='upgrade-btn'>
|
||||||
|
{props.loc}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
type ModalSnapshot = {
|
||||||
|
isShow: boolean
|
||||||
|
closable?: boolean
|
||||||
|
className?: string
|
||||||
|
}
|
||||||
|
let mockModalProps: ModalSnapshot | null = null
|
||||||
|
jest.mock('../../base/modal', () => ({
|
||||||
|
__esModule: true,
|
||||||
|
default: ({ isShow, children, onClose, closable, className }: { isShow: boolean; children: React.ReactNode; onClose: () => void; closable?: boolean; className?: string }) => {
|
||||||
|
mockModalProps = {
|
||||||
|
isShow,
|
||||||
|
closable,
|
||||||
|
className,
|
||||||
|
}
|
||||||
|
if (!isShow)
|
||||||
|
return null
|
||||||
|
return (
|
||||||
|
<div data-testid='annotation-full-modal' data-classname={className ?? ''}>
|
||||||
|
{closable && (
|
||||||
|
<button type='button' data-testid='mock-modal-close' onClick={onClose}>
|
||||||
|
close
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe('AnnotationFullModal', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
mockUsageProps = null
|
||||||
|
mockUpgradeBtnProps = null
|
||||||
|
mockModalProps = null
|
||||||
|
})
|
||||||
|
|
||||||
|
// Rendering marketing copy inside modal
|
||||||
|
describe('Rendering', () => {
|
||||||
|
it('should display main info when visible', () => {
|
||||||
|
// Act
|
||||||
|
render(<AnnotationFullModal show onHide={jest.fn()} />)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(screen.getByText('billing.annotatedResponse.fullTipLine1')).toBeInTheDocument()
|
||||||
|
expect(screen.getByText('billing.annotatedResponse.fullTipLine2')).toBeInTheDocument()
|
||||||
|
expect(screen.getByTestId('usage-component')).toHaveAttribute('data-classname', 'mt-4')
|
||||||
|
expect(screen.getByTestId('upgrade-btn')).toHaveTextContent('annotation-create')
|
||||||
|
expect(mockUpgradeBtnProps?.loc).toBe('annotation-create')
|
||||||
|
expect(mockModalProps).toEqual(expect.objectContaining({
|
||||||
|
isShow: true,
|
||||||
|
closable: true,
|
||||||
|
className: '!p-0',
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Controlling modal visibility
|
||||||
|
describe('Visibility', () => {
|
||||||
|
it('should not render content when hidden', () => {
|
||||||
|
// Act
|
||||||
|
const { container } = render(<AnnotationFullModal show={false} onHide={jest.fn()} />)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(container).toBeEmptyDOMElement()
|
||||||
|
expect(mockModalProps).toEqual(expect.objectContaining({ isShow: false }))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Handling close interactions
|
||||||
|
describe('Close handling', () => {
|
||||||
|
it('should trigger onHide when close control is clicked', () => {
|
||||||
|
// Arrange
|
||||||
|
const onHide = jest.fn()
|
||||||
|
|
||||||
|
// Act
|
||||||
|
render(<AnnotationFullModal show onHide={onHide} />)
|
||||||
|
fireEvent.click(screen.getByTestId('mock-modal-close'))
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(onHide).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import Item from './index'
|
||||||
|
|
||||||
|
describe('Item', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Rendering the plan item row
|
||||||
|
describe('Rendering', () => {
|
||||||
|
it('should render the provided label when tooltip is absent', () => {
|
||||||
|
// Arrange
|
||||||
|
const label = 'Monthly credits'
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const { container } = render(<Item label={label} />)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(screen.getByText(label)).toBeInTheDocument()
|
||||||
|
expect(container.querySelector('.group')).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Toggling the optional tooltip indicator
|
||||||
|
describe('Tooltip behavior', () => {
|
||||||
|
it('should render tooltip content when tooltip text is provided', () => {
|
||||||
|
// Arrange
|
||||||
|
const label = 'Workspace seats'
|
||||||
|
const tooltip = 'Seats define how many teammates can join the workspace.'
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const { container } = render(<Item label={label} tooltip={tooltip} />)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(screen.getByText(label)).toBeInTheDocument()
|
||||||
|
expect(screen.getByText(tooltip)).toBeInTheDocument()
|
||||||
|
expect(container.querySelector('.group')).not.toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should treat an empty tooltip string as absent', () => {
|
||||||
|
// Arrange
|
||||||
|
const label = 'Vector storage'
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const { container } = render(<Item label={label} tooltip='' />)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(screen.getByText(label)).toBeInTheDocument()
|
||||||
|
expect(container.querySelector('.group')).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { render, screen } from '@testing-library/react'
|
||||||
|
import Tooltip from './tooltip'
|
||||||
|
|
||||||
|
describe('Tooltip', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Rendering the info tooltip container
|
||||||
|
describe('Rendering', () => {
|
||||||
|
it('should render the content panel when provide with text', () => {
|
||||||
|
// Arrange
|
||||||
|
const content = 'Usage resets on the first day of every month.'
|
||||||
|
|
||||||
|
// Act
|
||||||
|
render(<Tooltip content={content} />)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(() => screen.getByText(content)).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Icon rendering', () => {
|
||||||
|
it('should render the icon when provided with content', () => {
|
||||||
|
// Arrange
|
||||||
|
const content = 'Tooltips explain each plan detail.'
|
||||||
|
|
||||||
|
// Act
|
||||||
|
render(<Tooltip content={content} />)
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(screen.getByTestId('tooltip-icon')).toBeInTheDocument()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Handling empty strings while keeping structure consistent
|
||||||
|
describe('Edge cases', () => {
|
||||||
|
it('should render without crashing when passed empty content', () => {
|
||||||
|
// Arrange
|
||||||
|
const content = ''
|
||||||
|
|
||||||
|
// Act and Assert
|
||||||
|
expect(() => render(<Tooltip content={content} />)).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
@ -8,13 +8,15 @@ type TooltipProps = {
|
||||||
const Tooltip = ({
|
const Tooltip = ({
|
||||||
content,
|
content,
|
||||||
}: TooltipProps) => {
|
}: TooltipProps) => {
|
||||||
|
if (!content)
|
||||||
|
return null
|
||||||
return (
|
return (
|
||||||
<div className='group relative z-10 size-[18px] overflow-visible'>
|
<div className='group relative z-10 size-[18px] overflow-visible'>
|
||||||
<div className='system-xs-regular absolute bottom-0 right-0 -z-10 hidden w-[260px] bg-saas-dify-blue-static px-5 py-[18px] text-text-primary-on-surface group-hover:block'>
|
<div className='system-xs-regular absolute bottom-0 right-0 -z-10 hidden w-[260px] bg-saas-dify-blue-static px-5 py-[18px] text-text-primary-on-surface group-hover:block'>
|
||||||
{content}
|
{content}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex h-full w-full items-center justify-center rounded-[4px] bg-state-base-hover transition-all duration-500 ease-in-out group-hover:rounded-none group-hover:bg-saas-dify-blue-static'>
|
<div className='flex h-full w-full items-center justify-center rounded-[4px] bg-state-base-hover transition-all duration-500 ease-in-out group-hover:rounded-none group-hover:bg-saas-dify-blue-static'>
|
||||||
<RiInfoI className='size-3.5 text-text-tertiary group-hover:text-text-primary-on-surface' />
|
<RiInfoI className='size-3.5 text-text-tertiary group-hover:text-text-primary-on-surface' data-testid="tooltip-icon" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue