fix: allow bare domains in URL validation and update related error messages
This commit is contained in:
parent
776c3bc96e
commit
3565503da4
|
|
@ -22,9 +22,11 @@ const validateAuditRequest = [
|
|||
body('name').trim().isLength({ min: 1, max: 100 }).escape(),
|
||||
body('company').trim().isLength({ min: 1, max: 150 }).escape(),
|
||||
body('email').trim().isEmail().normalizeEmail().isLength({ max: 254 }),
|
||||
// No require_protocol: people paste bare domains ("example.com") too.
|
||||
// protocols still pins an explicit scheme, if given, to http/https.
|
||||
body('url')
|
||||
.trim()
|
||||
.isURL({ protocols: ['http', 'https'], require_protocol: true })
|
||||
.isURL({ protocols: ['http', 'https'] })
|
||||
.isLength({ max: 2048 }),
|
||||
body('role').isIn(['agency', 'client']),
|
||||
body('motivation').trim().isLength({ min: 1, max: 2000 }).escape(),
|
||||
|
|
|
|||
|
|
@ -73,28 +73,25 @@ describe('AuditForm', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('rejects a URL without a protocol and wires the message to the field', async () => {
|
||||
it('accepts a bare domain without a protocol', async () => {
|
||||
const user = userEvent.setup();
|
||||
const fetchSpy = vi
|
||||
.spyOn(globalThis, 'fetch')
|
||||
.mockResolvedValue(new Response(null, { status: 200 }));
|
||||
renderForm();
|
||||
|
||||
await fillValidForm(user);
|
||||
const urlField = screen.getByLabelText('Welche Seite soll ich prüfen?');
|
||||
await user.clear(urlField);
|
||||
await user.type(urlField, 'example.de');
|
||||
await user.click(screen.getByRole('button', { name: 'Audit anfragen' }));
|
||||
|
||||
await waitFor(() => expect(urlField).toHaveAttribute('aria-invalid', 'true'));
|
||||
await waitFor(() => expect(fetchSpy).toHaveBeenCalledTimes(1));
|
||||
expect(urlField).not.toHaveAttribute('aria-invalid', 'true');
|
||||
|
||||
// The message deliberately appears twice - once in the error summary and
|
||||
// once at the field. Assert the one a screen reader reads out for the
|
||||
// field, reached through aria-describedby.
|
||||
const describedBy = urlField.getAttribute('aria-describedby') ?? '';
|
||||
const errorNode = describedBy
|
||||
.split(' ')
|
||||
.map((id) => document.getElementById(id))
|
||||
.find((node) => node?.className.includes('audit-form__error'));
|
||||
|
||||
expect(errorNode).toHaveTextContent(
|
||||
'Bitte gib die vollständige Adresse an, inklusive https://'
|
||||
);
|
||||
const [, init] = fetchSpy.mock.calls[0];
|
||||
const payload = JSON.parse(String(init?.body));
|
||||
expect(payload.url).toBe('example.de');
|
||||
});
|
||||
|
||||
it('posts to the configured endpoint and reports success', async () => {
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ export const audit = {
|
|||
urlLabel: 'Welche Seite soll ich prüfen?',
|
||||
urlHint: 'Die vollständige Adresse einer einzelnen Seite, zum Beispiel https://deine-firma.de/kontakt',
|
||||
urlErrorRequired: 'Bitte nenn mir die Seite, die ich prüfen soll.',
|
||||
urlErrorInvalid: 'Bitte gib die vollständige Adresse an, inklusive https://',
|
||||
|
||||
roleLegend: 'Wer bist du?',
|
||||
roleAgency: 'Agentur oder Dienstleister',
|
||||
|
|
|
|||
|
|
@ -376,7 +376,6 @@ export interface TextConfig {
|
|||
urlLabel: string;
|
||||
urlHint: string;
|
||||
urlErrorRequired: string;
|
||||
urlErrorInvalid: string;
|
||||
roleLegend: string;
|
||||
roleAgency: string;
|
||||
roleClient: string;
|
||||
|
|
|
|||
|
|
@ -96,7 +96,6 @@ export const audit = {
|
|||
urlLabel: 'Which page should I review?',
|
||||
urlHint: 'The full address of a single page, for example https://your-company.com/contact',
|
||||
urlErrorRequired: 'Please tell me which page to review.',
|
||||
urlErrorInvalid: 'Please give the full address, including https://',
|
||||
|
||||
roleLegend: 'Who are you?',
|
||||
roleAgency: 'Agency or service provider',
|
||||
|
|
|
|||
Loading…
Reference in New Issue