mirror of
https://mirror.skon.top/github.com/langgenius/dify.git
synced 2026-04-20 15:20:15 +08:00
Some checks failed
autofix.ci / autofix (push) Has been cancelled
Build and Push API & Web / build (api, {{defaultContext}}:api, Dockerfile, DIFY_API_IMAGE_NAME, linux/amd64, ubuntu-latest, build-api-amd64) (push) Has been cancelled
Build and Push API & Web / build (api, {{defaultContext}}:api, Dockerfile, DIFY_API_IMAGE_NAME, linux/arm64, ubuntu-24.04-arm, build-api-arm64) (push) Has been cancelled
Build and Push API & Web / build (web, {{defaultContext}}, web/Dockerfile, DIFY_WEB_IMAGE_NAME, linux/amd64, ubuntu-latest, build-web-amd64) (push) Has been cancelled
Build and Push API & Web / build (web, {{defaultContext}}, web/Dockerfile, DIFY_WEB_IMAGE_NAME, linux/arm64, ubuntu-24.04-arm, build-web-arm64) (push) Has been cancelled
Build and Push API & Web / create-manifest (api, DIFY_API_IMAGE_NAME, merge-api-images) (push) Has been cancelled
Build and Push API & Web / create-manifest (web, DIFY_WEB_IMAGE_NAME, merge-web-images) (push) Has been cancelled
Main CI Pipeline / Skip Duplicate Checks (push) Has been cancelled
Main CI Pipeline / Check Changed Files (push) Has been cancelled
Main CI Pipeline / Run API Tests (push) Has been cancelled
Main CI Pipeline / Skip API Tests (push) Has been cancelled
Main CI Pipeline / API Tests (push) Has been cancelled
Main CI Pipeline / Run Web Tests (push) Has been cancelled
Main CI Pipeline / Skip Web Tests (push) Has been cancelled
Main CI Pipeline / Web Tests (push) Has been cancelled
Main CI Pipeline / Run Web Full-Stack E2E (push) Has been cancelled
Main CI Pipeline / Skip Web Full-Stack E2E (push) Has been cancelled
Main CI Pipeline / Web Full-Stack E2E (push) Has been cancelled
Main CI Pipeline / Style Check (push) Has been cancelled
Main CI Pipeline / Run VDB Tests (push) Has been cancelled
Main CI Pipeline / Skip VDB Tests (push) Has been cancelled
Main CI Pipeline / VDB Tests (push) Has been cancelled
Main CI Pipeline / Run DB Migration Test (push) Has been cancelled
Main CI Pipeline / Skip DB Migration Test (push) Has been cancelled
Main CI Pipeline / DB Migration Test (push) Has been cancelled
Trigger i18n Sync on Push / trigger (push) Has been cancelled
Mark stale issues and pull requests / stale (push) Has been cancelled
190 lines
8.7 KiB
TypeScript
190 lines
8.7 KiB
TypeScript
import { render } from 'vitest-browser-react'
|
|
import { Switch, SwitchSkeleton } from '../index'
|
|
|
|
const asHTMLElement = (element: HTMLElement | SVGElement) => element as HTMLElement
|
|
const getThumb = (switchElement: HTMLElement | SVGElement) => switchElement.querySelector('span')
|
|
|
|
describe('Switch', () => {
|
|
it('should render in unchecked state when checked is false', async () => {
|
|
const screen = await render(<Switch checked={false} />)
|
|
const switchElement = screen.getByRole('switch')
|
|
|
|
await expect.element(switchElement).toBeInTheDocument()
|
|
await expect.element(switchElement).toHaveAttribute('aria-checked', 'false')
|
|
await expect.element(switchElement).not.toHaveAttribute('data-checked')
|
|
})
|
|
|
|
it('should render in checked state when checked is true', async () => {
|
|
const screen = await render(<Switch checked={true} />)
|
|
const switchElement = screen.getByRole('switch')
|
|
|
|
await expect.element(switchElement).toHaveAttribute('aria-checked', 'true')
|
|
await expect.element(switchElement).toHaveAttribute('data-checked', '')
|
|
})
|
|
|
|
it('should call onCheckedChange with next value when clicked', async () => {
|
|
const onCheckedChange = vi.fn()
|
|
const screen = await render(<Switch checked={false} onCheckedChange={onCheckedChange} />)
|
|
|
|
const switchElement = screen.getByRole('switch')
|
|
asHTMLElement(switchElement.element()).click()
|
|
|
|
expect(onCheckedChange).toHaveBeenCalledWith(true)
|
|
expect(onCheckedChange).toHaveBeenCalledTimes(1)
|
|
await expect.element(switchElement).toHaveAttribute('aria-checked', 'false')
|
|
})
|
|
|
|
it('should work in controlled mode with checked prop', async () => {
|
|
const onCheckedChange = vi.fn()
|
|
const screen = await render(<Switch checked={false} onCheckedChange={onCheckedChange} />)
|
|
const switchElement = screen.getByRole('switch')
|
|
|
|
await expect.element(switchElement).toHaveAttribute('aria-checked', 'false')
|
|
|
|
asHTMLElement(switchElement.element()).click()
|
|
expect(onCheckedChange).toHaveBeenCalledWith(true)
|
|
await expect.element(switchElement).toHaveAttribute('aria-checked', 'false')
|
|
|
|
await screen.rerender(<Switch checked={true} onCheckedChange={onCheckedChange} />)
|
|
await expect.element(screen.getByRole('switch')).toHaveAttribute('aria-checked', 'true')
|
|
})
|
|
|
|
it('should not call onCheckedChange when disabled', async () => {
|
|
const onCheckedChange = vi.fn()
|
|
const screen = await render(<Switch checked={false} disabled onCheckedChange={onCheckedChange} />)
|
|
|
|
const switchElement = screen.getByRole('switch')
|
|
await expect.element(switchElement).toHaveClass('data-disabled:cursor-not-allowed')
|
|
await expect.element(switchElement).toHaveAttribute('data-disabled', '')
|
|
|
|
asHTMLElement(switchElement.element()).click()
|
|
expect(onCheckedChange).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('should apply correct size classes', async () => {
|
|
const screen = await render(<Switch checked={false} size="xs" />)
|
|
|
|
await expect.element(screen.getByRole('switch')).toHaveClass('h-2.5', 'w-3.5', 'rounded-xs')
|
|
|
|
await screen.rerender(<Switch checked={false} size="sm" />)
|
|
await expect.element(screen.getByRole('switch')).toHaveClass('h-3', 'w-5')
|
|
|
|
await screen.rerender(<Switch checked={false} size="md" />)
|
|
await expect.element(screen.getByRole('switch')).toHaveClass('h-4', 'w-7')
|
|
|
|
await screen.rerender(<Switch checked={false} size="lg" />)
|
|
await expect.element(screen.getByRole('switch')).toHaveClass('h-5', 'w-9')
|
|
})
|
|
|
|
it('should apply custom className', async () => {
|
|
const screen = await render(<Switch checked={false} className="custom-test-class" />)
|
|
await expect.element(screen.getByRole('switch')).toHaveClass('custom-test-class')
|
|
})
|
|
|
|
it('should expose checked state styling hooks on the root and thumb', async () => {
|
|
const screen = await render(<Switch checked={false} />)
|
|
const switchElement = screen.getByRole('switch').element()
|
|
const thumb = getThumb(switchElement)
|
|
|
|
expect(switchElement).toHaveClass('bg-components-toggle-bg-unchecked', 'data-checked:bg-components-toggle-bg')
|
|
expect(thumb).toHaveClass('data-checked:translate-x-[14px]')
|
|
expect(thumb).not.toHaveAttribute('data-checked')
|
|
|
|
await screen.rerender(<Switch checked={true} />)
|
|
|
|
await expect.element(screen.getByRole('switch')).toHaveAttribute('data-checked', '')
|
|
expect(getThumb(screen.getByRole('switch').element())).toHaveAttribute('data-checked', '')
|
|
})
|
|
|
|
it('should expose disabled state styling hooks instead of relying on opacity', async () => {
|
|
const screen = await render(<Switch checked={false} disabled />)
|
|
|
|
await expect.element(screen.getByRole('switch')).toHaveClass(
|
|
'data-disabled:bg-components-toggle-bg-unchecked-disabled',
|
|
'data-disabled:data-checked:bg-components-toggle-bg-disabled',
|
|
)
|
|
await expect.element(screen.getByRole('switch')).toHaveAttribute('data-disabled', '')
|
|
|
|
await screen.rerender(<Switch checked={true} disabled />)
|
|
await expect.element(screen.getByRole('switch')).toHaveAttribute('data-disabled', '')
|
|
await expect.element(screen.getByRole('switch')).toHaveAttribute('data-checked', '')
|
|
})
|
|
|
|
it('should have focus-visible ring-3 styles', async () => {
|
|
const screen = await render(<Switch checked={false} />)
|
|
await expect.element(screen.getByRole('switch')).toHaveClass('focus-visible:ring-2')
|
|
})
|
|
|
|
it('should respect prefers-reduced-motion', async () => {
|
|
const screen = await render(<Switch checked={false} />)
|
|
await expect.element(screen.getByRole('switch')).toHaveClass('motion-reduce:transition-none')
|
|
})
|
|
|
|
describe('loading state', () => {
|
|
it('should render as disabled when loading', async () => {
|
|
const onCheckedChange = vi.fn()
|
|
const screen = await render(<Switch checked={false} loading onCheckedChange={onCheckedChange} />)
|
|
|
|
const switchElement = screen.getByRole('switch')
|
|
await expect.element(switchElement).toHaveClass('data-disabled:cursor-not-allowed')
|
|
await expect.element(switchElement).toHaveAttribute('aria-busy', 'true')
|
|
await expect.element(switchElement).toHaveAttribute('data-disabled', '')
|
|
|
|
asHTMLElement(switchElement.element()).click()
|
|
expect(onCheckedChange).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('should show spinner icon for md and lg sizes', async () => {
|
|
const screen = await render(<Switch checked={false} loading size="md" />)
|
|
expect(screen.container.querySelector('span[aria-hidden="true"] i')).toBeInTheDocument()
|
|
|
|
await screen.rerender(<Switch checked={false} loading size="lg" />)
|
|
expect(screen.container.querySelector('span[aria-hidden="true"] i')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should not show spinner for xs and sm sizes', async () => {
|
|
const screen = await render(<Switch checked={false} loading size="xs" />)
|
|
expect(screen.container.querySelector('span[aria-hidden="true"] i')).not.toBeInTheDocument()
|
|
|
|
await screen.rerender(<Switch checked={false} loading size="sm" />)
|
|
expect(screen.container.querySelector('span[aria-hidden="true"] i')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('should apply disabled data-state hooks when loading', async () => {
|
|
const screen = await render(<Switch checked={false} loading />)
|
|
|
|
await expect.element(screen.getByRole('switch')).toHaveAttribute('data-disabled', '')
|
|
|
|
await screen.rerender(<Switch checked={true} loading />)
|
|
await expect.element(screen.getByRole('switch')).toHaveAttribute('data-disabled', '')
|
|
await expect.element(screen.getByRole('switch')).toHaveAttribute('data-checked', '')
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('SwitchSkeleton', () => {
|
|
it('should render a plain div without switch role', async () => {
|
|
const screen = await render(<SwitchSkeleton data-testid="skeleton-switch" />)
|
|
expect(screen.container.querySelector('[role="switch"]')).not.toBeInTheDocument()
|
|
await expect.element(screen.getByTestId('skeleton-switch')).toBeInTheDocument()
|
|
})
|
|
|
|
it('should apply skeleton styles', async () => {
|
|
const screen = await render(<SwitchSkeleton data-testid="skeleton-switch" />)
|
|
await expect.element(screen.getByTestId('skeleton-switch')).toHaveClass('bg-text-quaternary', 'opacity-20')
|
|
})
|
|
|
|
it('should apply correct skeleton size classes', async () => {
|
|
const screen = await render(<SwitchSkeleton size="xs" data-testid="s" />)
|
|
await expect.element(screen.getByTestId('s')).toHaveClass('h-2.5', 'w-3.5', 'rounded-xs')
|
|
|
|
await screen.rerender(<SwitchSkeleton size="lg" data-testid="s" />)
|
|
await expect.element(screen.getByTestId('s')).toHaveClass('h-5', 'w-9', 'rounded-md')
|
|
})
|
|
|
|
it('should apply custom className to skeleton', async () => {
|
|
const screen = await render(<SwitchSkeleton className="custom-class" data-testid="s" />)
|
|
await expect.element(screen.getByTestId('s')).toHaveClass('custom-class')
|
|
})
|
|
})
|