mirror of
https://github.com/langgenius/dify.git
synced 2026-09-21 13:20:52 +08:00
fix(web): remove misleading ARIA states (#40015)
This commit is contained in:
@@ -352,8 +352,9 @@ describe('ConfigurationView', () => {
|
||||
|
||||
render(<ConfigurationView {...createViewModel({ contextValue })} />)
|
||||
|
||||
const badge = screen.getByRole('button', { name: 'appDebug.legacyAgentBadge.description' })
|
||||
const badge = screen.getByRole('button', { name: 'appDebug.legacyAgentBadge.label' })
|
||||
expect(badge).toHaveTextContent('appDebug.legacyAgentBadge.label')
|
||||
expect(badge).not.toHaveAttribute('aria-label')
|
||||
|
||||
fireEvent.click(badge)
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ function LegacyAgentBadge() {
|
||||
delay={300}
|
||||
closeDelay={200}
|
||||
type="button"
|
||||
aria-label={description}
|
||||
className="inline-flex h-5 shrink-0 cursor-pointer items-center gap-0.5 rounded-[5px] border border-text-warning bg-components-badge-bg-dimm px-1.25 system-2xs-medium-uppercase whitespace-nowrap text-text-warning outline-hidden hover:bg-state-warning-hover focus-visible:ring-2 focus-visible:ring-state-accent-solid"
|
||||
>
|
||||
<span aria-hidden className="i-ri-alert-fill size-3 shrink-0" />
|
||||
|
||||
@@ -456,7 +456,7 @@ describe('AppCard', () => {
|
||||
|
||||
const card = screen.getByRole('button', { name: 'Preview Only App' })
|
||||
expect(card).toHaveClass('opacity-60')
|
||||
expect(card).toHaveAttribute('aria-disabled', 'true')
|
||||
expect(card).not.toHaveAttribute('aria-disabled')
|
||||
expect(screen.getByText('Only visible metadata')).toBeInTheDocument()
|
||||
expect(screen.getByText('Readonly Author')).toBeInTheDocument()
|
||||
const tagSelector = screen.getByLabelText('tag-selector')
|
||||
@@ -495,7 +495,7 @@ describe('AppCard', () => {
|
||||
|
||||
const card = screen.getByRole('button', { name: 'Preview Only Starred App' })
|
||||
expect(card).toHaveClass('opacity-60')
|
||||
expect(card).toHaveAttribute('aria-disabled', 'true')
|
||||
expect(card).not.toHaveAttribute('aria-disabled')
|
||||
expect(screen.getByText('Readonly Author')).toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByRole('link', { name: 'Preview Only Starred App' }),
|
||||
|
||||
@@ -1204,7 +1204,6 @@ export function AppCard({
|
||||
<article
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-disabled="true"
|
||||
aria-labelledby={appNameId}
|
||||
aria-describedby={app.description ? appDescriptionId : undefined}
|
||||
data-step-by-step-tour-target={stepByStepTourCardTarget}
|
||||
|
||||
@@ -106,7 +106,6 @@ export function StarredAppCard({
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
aria-disabled="true"
|
||||
aria-label={app.name}
|
||||
data-step-by-step-tour-target={stepByStepTourCardTarget}
|
||||
data-step-by-step-tour-highlight-part={stepByStepTourCardHighlightPart}
|
||||
|
||||
@@ -14,6 +14,7 @@ describe('AudioPreview', () => {
|
||||
const audio = document.querySelector('audio')
|
||||
expect(audio).toBeInTheDocument()
|
||||
expect(audio).toHaveAttribute('title', 'Test Audio')
|
||||
expect(audio?.parentElement).not.toHaveAttribute('aria-label')
|
||||
})
|
||||
|
||||
it('should render source element with correct src and type', () => {
|
||||
|
||||
@@ -64,6 +64,7 @@ describe('PdfPreview', () => {
|
||||
render(<PdfPreview url="https://example.com/doc.pdf" onCancel={mockOnCancel} />)
|
||||
|
||||
expect(document.querySelector('[tabindex="-1"]')).toBeInTheDocument()
|
||||
expect(getScaleContainer()).not.toHaveAttribute('aria-label')
|
||||
expect(screen.getByTestId('pdf-loader')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('pdf-highlighter')).toBeInTheDocument()
|
||||
expect(screen.getByRole('status')).toBeInTheDocument()
|
||||
|
||||
@@ -14,6 +14,7 @@ describe('VideoPreview', () => {
|
||||
const video = document.querySelector('video')
|
||||
expect(video).toBeInTheDocument()
|
||||
expect(video).toHaveAttribute('title', 'Test Video')
|
||||
expect(video?.parentElement).not.toHaveAttribute('aria-label')
|
||||
})
|
||||
|
||||
it('should render source element with correct src and type', () => {
|
||||
|
||||
@@ -22,7 +22,7 @@ const AudioPreview: FC<AudioPreviewProps> = ({ url, title, onCancel }) => {
|
||||
className="inset-0! top-0! left-0! flex h-dvh! max-h-none! w-screen! max-w-none! translate-0! items-center justify-center overflow-hidden! rounded-none! border-none! bg-black/80 p-8! shadow-none!"
|
||||
backdropClassName="bg-transparent!"
|
||||
>
|
||||
<div aria-label={title} tabIndex={-1} onClick={(e) => e.stopPropagation()}>
|
||||
<div tabIndex={-1} onClick={(e) => e.stopPropagation()}>
|
||||
<audio controls title={title} autoPlay={false} preload="metadata">
|
||||
<source type="audio/mpeg" src={url} className="max-h-full max-w-full" />
|
||||
</audio>
|
||||
|
||||
@@ -57,7 +57,6 @@ const PdfPreview: FC<PdfPreviewProps> = ({ url, onCancel }) => {
|
||||
backdropClassName="bg-transparent!"
|
||||
>
|
||||
<div
|
||||
aria-label={url}
|
||||
tabIndex={-1}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="h-[95vh] max-h-full w-screen max-w-full overflow-hidden"
|
||||
|
||||
@@ -22,7 +22,7 @@ const VideoPreview: FC<VideoPreviewProps> = ({ url, title, onCancel }) => {
|
||||
className="inset-0! top-0! left-0! flex h-dvh! max-h-none! w-screen! max-w-none! translate-0! items-center justify-center overflow-hidden! rounded-none! border-none! bg-black/80 p-8! shadow-none!"
|
||||
backdropClassName="bg-transparent!"
|
||||
>
|
||||
<div aria-label={title} tabIndex={-1} onClick={(e) => e.stopPropagation()}>
|
||||
<div tabIndex={-1} onClick={(e) => e.stopPropagation()}>
|
||||
<video controls title={title} autoPlay={false} preload="metadata">
|
||||
<source type="video/mp4" src={url} className="max-h-full max-w-full" />
|
||||
</video>
|
||||
|
||||
@@ -99,6 +99,7 @@ describe('ImagePreview', () => {
|
||||
|
||||
const overlay = getOverlay()
|
||||
expect(overlay).toBeInTheDocument()
|
||||
expect(screen.getByTestId('image-preview-container')).not.toHaveAttribute('aria-label')
|
||||
expect(overlay.closest('[data-base-ui-portal]')?.parentElement).toBe(document.body)
|
||||
expect(screen.getByRole('img', { name: 'Preview Image' })).toHaveAttribute(
|
||||
'src',
|
||||
|
||||
@@ -185,7 +185,6 @@ const ImagePreview: FC<ImagePreviewProps> = ({ url, title, onCancel, onPrev, onN
|
||||
backdropClassName="bg-transparent!"
|
||||
>
|
||||
<div
|
||||
aria-label={title}
|
||||
data-testid="image-preview-container"
|
||||
tabIndex={-1}
|
||||
className="flex size-full items-center justify-center"
|
||||
|
||||
@@ -383,7 +383,7 @@ describe('DatasetCard Component', () => {
|
||||
|
||||
const card = screen.getByRole('button', { name: 'Preview Only Dataset' })
|
||||
expect(card).toHaveClass('opacity-60')
|
||||
expect(card).toHaveAttribute('aria-disabled', 'true')
|
||||
expect(card).not.toHaveAttribute('aria-disabled')
|
||||
expect(screen.getByText('Preview Only Dataset')).toBeInTheDocument()
|
||||
const tagArea = screen.getByTestId('tag-area')
|
||||
expect(tagArea).toHaveAttribute('data-can-bind-or-unbind-tags', 'false')
|
||||
|
||||
@@ -120,7 +120,6 @@ const DatasetCard = ({
|
||||
<div
|
||||
role={isPreviewOnly ? 'button' : undefined}
|
||||
tabIndex={isPreviewOnly ? 0 : undefined}
|
||||
aria-disabled={isPreviewOnly ? 'true' : undefined}
|
||||
aria-label={isPreviewOnly ? dataset.name : undefined}
|
||||
className={cardClassName}
|
||||
data-disable-nprogress={true}
|
||||
|
||||
+3
-1
@@ -307,7 +307,9 @@ describe('PopupItem', () => {
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByText('GPT-4'))
|
||||
fireEvent.click(screen.getByText('COMMON.OPERATION.ADD'))
|
||||
const addButton = screen.getByRole('button', { name: 'COMMON.OPERATION.ADD' })
|
||||
expect(addButton.closest('[aria-disabled="true"]')).toBeNull()
|
||||
fireEvent.click(addButton)
|
||||
|
||||
expect(onValueChange).not.toHaveBeenCalled()
|
||||
expect(mockSetShowModelModal).toHaveBeenCalled()
|
||||
|
||||
+1
-1
@@ -225,7 +225,7 @@ function PopupItem({
|
||||
)
|
||||
const itemRender =
|
||||
modelItem.status === ModelStatusEnum.noConfigure ? (
|
||||
<div className={rowClassName} aria-disabled="true" onPointerDown={onPreviewCardClose}>
|
||||
<div className={rowClassName} onPointerDown={onPreviewCardClose}>
|
||||
{rowContent}
|
||||
{canCreateCredential && (
|
||||
<button
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { screen, waitFor } from '@testing-library/react'
|
||||
import { fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { useRouter, useSearchParams } from '@/next/navigation'
|
||||
import { renderWithConsoleQuery as render } from '@/test/console/query-data'
|
||||
@@ -205,4 +205,23 @@ describe('NormalForm', () => {
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
it('should describe password visibility through its changing action name', () => {
|
||||
mockQueryResults(
|
||||
nonInviteQueryResult as unknown as ReturnType<typeof useQuery>,
|
||||
nonInviteQueryResult as unknown as ReturnType<typeof useQuery>,
|
||||
)
|
||||
|
||||
render(<NormalForm />)
|
||||
|
||||
const showPasswordButton = screen.getByRole('button', { name: 'login.showPassword' })
|
||||
expect(showPasswordButton).not.toHaveAttribute('aria-pressed')
|
||||
|
||||
fireEvent.click(showPasswordButton)
|
||||
|
||||
expect(screen.getByRole('button', { name: 'login.hidePassword' })).not.toHaveAttribute(
|
||||
'aria-pressed',
|
||||
)
|
||||
expect(screen.getByLabelText('login.password')).toHaveAttribute('type', 'text')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -156,7 +156,6 @@ export default function MailAndPasswordAuth({ isInvite, isEmailSetup }: MailAndP
|
||||
aria-label={t(($) => $[showPassword ? 'hidePassword' : 'showPassword'], {
|
||||
ns: 'login',
|
||||
})}
|
||||
aria-pressed={showPassword}
|
||||
className="mr-1 size-8 p-0 text-text-tertiary hover:text-text-secondary"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
>
|
||||
|
||||
@@ -138,14 +138,14 @@ describe('ContinueWorkItem', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('should render preview-only apps as disabled buttons and warn on click', async () => {
|
||||
it('should render preview-only apps as action buttons that explain the access restriction', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderItem(createApp({ permission_keys: [AppACLPermission.Preview] }))
|
||||
|
||||
const card = screen.getByRole('button', { name: /Continue App.*app\.types\.chatbot/i })
|
||||
|
||||
expect(card).toHaveAttribute('type', 'button')
|
||||
expect(card).toHaveAttribute('aria-disabled', 'true')
|
||||
expect(card).not.toHaveAttribute('aria-disabled')
|
||||
expect(card).toHaveAccessibleDescription(/Alice.*5 minutes ago/)
|
||||
expect(screen.queryByRole('link', { name: /Continue App/ })).not.toBeInTheDocument()
|
||||
|
||||
|
||||
@@ -110,7 +110,6 @@ export function ContinueWorkItem({ app }: ContinueWorkItemProps) {
|
||||
type="button"
|
||||
aria-labelledby={`${appNameId} ${appModeId}`}
|
||||
aria-describedby={appMetadataId}
|
||||
aria-disabled="true"
|
||||
className="absolute inset-0 z-10 cursor-not-allowed touch-manipulation appearance-none rounded-xl border-0 bg-transparent p-0 outline-hidden focus-visible:inset-ring-2 focus-visible:inset-ring-state-accent-solid"
|
||||
onClick={showPreviewOnlyAccessWarning}
|
||||
/>
|
||||
|
||||
@@ -770,7 +770,7 @@ describe('HomeContent', () => {
|
||||
renderHomeContent()
|
||||
|
||||
const card = screen.getByRole('button', { name: /Preview Only App.*app\.types\.chatbot/ })
|
||||
expect(card).toHaveAttribute('aria-disabled', 'true')
|
||||
expect(card).not.toHaveAttribute('aria-disabled')
|
||||
expect(screen.queryByRole('link', { name: /Preview Only App/ })).not.toBeInTheDocument()
|
||||
expect(screen.getByText('Readonly Author')).toBeInTheDocument()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user