fix(dns): update Script Path input accessibility and placeholder for script provider

This commit is contained in:
GitHub Actions
2026-02-01 00:04:57 +00:00
parent 38596d9dff
commit 3c0fbaeba8
5 changed files with 402 additions and 12 deletions

View File

@@ -287,16 +287,31 @@ export default function DNSProviderForm({
// Default: text or password input fields
return (
<Input
key={field.name}
label={field.label}
type={field.type}
value={credentials[field.name] || ''}
onChange={(e) => handleCredentialChange(field.name, e.target.value)}
placeholder={field.placeholder || field.default}
helperText={field.hint}
required={field.required && !provider}
/>
<div key={field.name} className="space-y-1.5">
<Label htmlFor={`field-${field.name}`}>{field.label}</Label>
<Input
id={`field-${field.name}`}
aria-label={
field.name === 'create_script' && providerType === 'script'
? 'Script Path'
: undefined
}
type={field.type}
value={credentials[field.name] || ''}
onChange={(e) => handleCredentialChange(field.name, e.target.value)}
placeholder={
field.name === 'create_script' && providerType === 'script'
? '/scripts/dns-challenge.sh'
: field.placeholder || field.default
}
required={field.required && !provider}
/>
{field.hint && (
<p id={`hint-${field.name}`} className="text-sm text-content-muted">
{field.hint}
</p>
)}
</div>
)
})}
</div>

View File

@@ -0,0 +1,77 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import DNSProviderForm from '../DNSProviderForm'
import { defaultProviderSchemas } from '../../data/dnsProviderSchemas'
// Mock hooks used by DNSProviderForm
vi.mock('../../hooks/useDNSProviders', () => ({
useDNSProviderTypes: vi.fn(() => ({ data: [defaultProviderSchemas.script], isLoading: false })),
useDNSProviderMutations: vi.fn(() => ({ createMutation: { isPending: false }, updateMutation: { isPending: false }, testCredentialsMutation: { isPending: false } })),
}))
vi.mock('../../hooks/usePlugins', () => ({
useProviderFields: vi.fn(() => ({ data: undefined })),
}))
vi.mock('../../hooks/useCredentials', () => ({ useCredentials: vi.fn(() => ({ data: [] })) }))
vi.mock('../../hooks/useEnableMultiCredentials', () => ({ useEnableMultiCredentials: vi.fn(() => ({}) ) }))
const renderWithClient = (ui: React.ReactElement) => {
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } })
return render(<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>)
}
describe('DNSProviderForm — Script provider (accessibility)', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('renders `Script Path` input when Script provider is selected (add flow)', async () => {
renderWithClient(<DNSProviderForm open={true} onOpenChange={() => {}} provider={null} onSuccess={() => {}} />)
// Open provider selector and choose the script provider
const select = screen.getByLabelText(/provider type/i)
await userEvent.click(select)
const scriptOption = await screen.findByRole('option', { name: /script|custom script/i })
await userEvent.click(scriptOption)
// The input should be present, labelled "Script Path", have the expected placeholder and be required (add flow)
const scriptInput = await screen.findByRole('textbox', { name: /script path/i })
expect(scriptInput).toBeInTheDocument()
expect(scriptInput).toHaveAttribute('placeholder', expect.stringMatching(/dns-challenge\.sh/i))
expect(scriptInput).toBeRequired()
// Keyboard focus works
scriptInput.focus()
await waitFor(() => expect(scriptInput).toHaveFocus())
})
it('renders Script Path when editing an existing script provider (not required)', async () => {
const existingProvider = {
id: 1,
uuid: 'p-1',
name: 'local-script',
provider_type: 'script',
enabled: true,
is_default: false,
has_credentials: true,
propagation_timeout: 120,
polling_interval: 5,
success_count: 0,
failure_count: 0,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
}
renderWithClient(
<DNSProviderForm open={true} onOpenChange={() => {}} provider={existingProvider as any} onSuccess={() => {}} />
)
// Since provider prop is provided, providerType should be pre-populated and the field rendered
const scriptInput = await screen.findByRole('textbox', { name: /script path/i })
expect(scriptInput).toBeInTheDocument()
// Not required when editing
expect(scriptInput).not.toBeRequired()
})
})

View File

@@ -217,10 +217,10 @@ export const defaultProviderSchemas: Record<DNSProviderType, Partial<DNSProvider
fields: [
{
name: 'create_script',
label: 'Create Record Script',
label: 'Script Path',
type: 'text',
required: true,
placeholder: '/path/to/create-dns.sh',
placeholder: '/scripts/dns-challenge.sh',
hint: 'Path to script that creates DNS TXT records. Receives DOMAIN, TOKEN, and FQDN as environment variables.',
},
{