Repository navigation
Restore component-level custom validation message overrides - #475
Merged
alexluckett merged 20 commits intoOct 23, 2026
Merged
Conversation
…ides Checks every component type against options.customValidationMessage(s), with and without a translator. 5 components lose the override at request time because getValidationMessagesOverride() always replaces the message.
…esent getValidationMessagesOverride() always replaced Joi's messages with the engine's translated defaults, so a form author's customValidationMessage(s) was silently dropped on real requests, where a translator is always passed. The author's message now wins for any key they set.
The keep-the-author's-message filter was duplicated across four components. Moved it into withCustomValidationOverrides(), which also holds the one type workaround for Joi's LanguageMessages type not covering JoiExpression values, instead of repeating the cast per file.
…st coverage withCustomValidationOverrides() now also skips translated defaults when a single customValidationMessage is set, instead of each component repeating that check. Added singular-message tests for the six components that support it but had only plural coverage, confirming the fix against a v4.23.0 baseline for both forms of the option.
withCustomValidationOverrides() now builds the override for every key in the schema instead of returning null and relying on the constructor having already set the same keys. This removes the implicit assumption that the constructor's key list matches the translated defaults, and callers no longer need to handle a null result.
Support for options.customValidationMessage (singular) is not the same as support for options.customValidationMessages (plural): 3 components support only the plural form, and 17 don't even declare the singular field on their options type, so the two were being conflated in one matrix. Each option now has its own file, its own supported/not-supported lists, and its own coverage guard test.
Splitting the singular and plural test files left getCompositeFormData() copy-pasted in both. Moved it to test/helpers/component-helpers.js, next to getFormData(), so it exists once.
…ion-message-override-tests
TrackingField came in from main. Its options type declares customValidationMessages through FormFieldBase but the component never reads it, so it is listed as not supported for the plural option and as having no singular option.
The coverage guard tests now compare against form component types only, using isFormType from @defra/forms-model, so that content-only types are classified in one place rather than repeated in each test file.
Each test file now runs one supported test and one not-supported test for every type in its lists, rather than a hand-written block per component. A fixture table holds only what differs by type: extra definition fields and the empty input for composite fields. The not-supported test also checks that the baseline has errors, so that a wrong fixture cannot pass with no errors on either side.
Composite fields and GeospatialField give a validation error with empty form data, so their fixture entries and the getCompositeFormData test helper are removed.
The extra definition fields, inputs and model setup move to test/helpers/validation-helpers.ts, so that both test files use one copy. Each extra definition entry is typed as a partial of its own component definition from @defra/forms-model, so that a field that does not belong to the type is a type error. The helper is in test/ rather than src/, so that the type build, which compiles only src/, does not include test files.
An if/else-if chain without a final else is a Sonar code smell, so the global override returns early and the defaults use a plain if.
alexluckett
marked this pull request as ready for review
October 22, 2026 23:13
jbarnsley10
reviewed
Oct 23, 2026
jbarnsley10
approved these changes
Oct 23, 2026
jbarnsley10
left a comment
Contributor
There was a problem hiding this comment.
Looks good. Just that question about single message for Autocomplete, and there are two minor Sonar issues. Approving in principle
alexluckett
enabled auto-merge
October 23, 2026 10:58
|
alexluckett
deleted the
bugfix/custom-validation-message-override-tests
branch
October 23, 2026 11:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



A v5 change stopped
options.customValidationMessageandoptions.customValidationMessagesfrom working for some components. This PR makes these options work again, and adds tests for each form component type to ensure it doesn't break again.Architecture
ComponentCollection.validate()calls thegetValidationMessagesOverride(translator)method of a component only when it gets a translator. A real request always gives a translator. The old unit tests did not give one, so they did not find the problem. Some components use this method to translate the default English messages. The method replaced all messages in the schema, so it removed the messages that the form author set. The new helperwithCustomValidationOverrides()(components/helpers/index.ts) ensures that they are still supplied, even when a translator is provided.Key changes
NumberField,LocationFieldBase(forOsGridRefFieldandNationalGridFieldNumberField),AutocompleteFieldandYesNoFielduse the new helper. Before, they always returned the translated defaults.customValidationMessage.test.tsfor the singular option andcustomValidationMessages.test.tsfor the map. Each file has a list of the types that support the option and a list of the types that do not.