diff --git a/src/formio/validators/plugins.js b/src/formio/validators/plugins.js index 129617a42..625bf7c17 100644 --- a/src/formio/validators/plugins.js +++ b/src/formio/validators/plugins.js @@ -1,41 +1,30 @@ import {post} from '../../api'; -const errorMessageMap = { - 'kvk-kvkNumber': 'Invalid Kvk Number', - 'kvk-rsin': 'Invalid RSIN', - 'kvk-branchNumber': 'Invalid Branch Number', - 'phonenumber-international': 'Invalid international phonenumber', - 'phonenumber-nl': 'Invalid Dutch phonenumber', -}; +export const pluginsAPIValidator = { + key: `validate.backendApi`, + check(component, setting, value) { + if (!value) return true; -const pluginAPIValidator = plugin => { - let defaultMsg = errorMessageMap[plugin]; - // catches undefined too - if (defaultMsg == null) { - defaultMsg = 'Invalid'; - } + const plugins = component.component.validate.plugins; + const {baseUrl} = component.currentForm?.options || component.options; - return { - key: `validate.${plugin}`, - message(component) { - return component.t(component.errorMessage(defaultMsg), { - field: component.errorLabel, - data: component.data, + const promises = plugins.map(plugin => { + const url = `${baseUrl}validation/plugins/${plugin}`; + return post(url, {value}).then(response => { + const valid = response.data.isValid; + return valid ? true : response.data.messages.join('
'); }); - }, - check(component, setting, value) { - if (!value) return true; + }); + return Promise.all(promises) + .then(results => { + const anyValid = results.some(result => result === true); - const {baseUrl} = component.currentForm?.options || component.options; - const url = `${baseUrl}validation/plugins/${plugin}`; - return post(url, {value}) - .then(response => { - const valid = response.data.isValid; - return valid ? true : response.data.messages.join('
'); - }) - .catch(() => false); - }, - }; + if (anyValid) return true; + + return results.join('
'); + }) + .catch(() => false); + }, }; /** @@ -46,13 +35,9 @@ const pluginAPIValidator = plugin => { */ const enableValidationPlugins = component => { if (Array.isArray(component.component.validate.plugins)) { - for (let plugin of component.component.validate.plugins) { - const validator = pluginAPIValidator(plugin); - if (validator == null) continue; - component.component.validateOn = 'blur'; - component.validator.validators[plugin] = validator; - component.validators.push(plugin); - } + component.component.validateOn = 'blur'; + component.validator.validators.backendApi = pluginsAPIValidator; + component.validators.push('backendApi'); } }; diff --git a/src/jstests/formio/components/fixtures/phonenumber.js b/src/jstests/formio/components/fixtures/phonenumber.js new file mode 100644 index 000000000..effad92bb --- /dev/null +++ b/src/jstests/formio/components/fixtures/phonenumber.js @@ -0,0 +1,15 @@ +const phoneNumberComponent = { + label: 'Phone Number', + key: 'phonenumber', + type: 'phoneNumber', + input: true, + validate: { + backendApi: true, + plugins: ['phonenumber-international', 'phonenumber-nl'], + }, +}; + +const validSamples = ['0630123456', '+31630123456']; +const inValidSamples = ['63012345', '+3163012345']; + +export {phoneNumberComponent, validSamples, inValidSamples}; diff --git a/src/jstests/formio/validators/mocks.js b/src/jstests/formio/validators/mocks.js new file mode 100644 index 000000000..4f96aae89 --- /dev/null +++ b/src/jstests/formio/validators/mocks.js @@ -0,0 +1,36 @@ +import {rest} from 'msw'; + +import {BASE_URL} from 'api-mocks'; + +const INTERNATIONAL_VALIDATION_ENDPOINT = `${BASE_URL}validation/plugins/phonenumber-international`; +const DUTCH_VALIDATION_ENDPOINT = `${BASE_URL}validation/plugins/phonenumber-nl`; + +export const phoneNumberValidations = { + mockValidInternationalPhonenumberPost: rest.post( + INTERNATIONAL_VALIDATION_ENDPOINT, + async (req, res, ctx) => { + return res(ctx.status(200), ctx.json({isValid: true, messages: []})); + } + ), + + mockInValidInternationalPhonenumberPost: rest.post( + INTERNATIONAL_VALIDATION_ENDPOINT, + async (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({isValid: false, messages: ['Invalid international phone number']}) + ); + } + ), + + mockValidDutchPhonenumberPost: rest.post(DUTCH_VALIDATION_ENDPOINT, async (req, res, ctx) => { + return res(ctx.status(200), ctx.json({isValid: true, messages: []})); + }), + + mockInValidDutchPhonenumberPost: rest.post(DUTCH_VALIDATION_ENDPOINT, async (req, res, ctx) => { + return res( + ctx.status(200), + ctx.json({isValid: false, messages: ['Invalid dutch phone number']}) + ); + }), +}; diff --git a/src/jstests/formio/validators/pluginapivalidator.spec.js b/src/jstests/formio/validators/pluginapivalidator.spec.js new file mode 100644 index 000000000..8e18bfae2 --- /dev/null +++ b/src/jstests/formio/validators/pluginapivalidator.spec.js @@ -0,0 +1,68 @@ +import { + inValidSamples, + phoneNumberComponent, + validSamples, +} from 'jstests/formio/components/fixtures/phonenumber'; + +import {BASE_URL} from 'api-mocks'; +import mswServer from 'api-mocks/msw-server'; +import {pluginsAPIValidator} from 'formio/validators/plugins'; + +import {phoneNumberValidations} from './mocks'; + +describe('The OpenForms plugins validation', () => { + test('tests expected errors are returned when phone number is invalid', async () => { + mswServer.use( + phoneNumberValidations.mockInValidDutchPhonenumberPost, + phoneNumberValidations.mockInValidInternationalPhonenumberPost + ); + + const component = { + component: phoneNumberComponent, + options: { + baseUrl: BASE_URL, + }, + }; + + for (const sample of inValidSamples) { + const result = await pluginsAPIValidator.check(component, undefined, sample); + expect(result).toBe('Invalid international phone number
Invalid dutch phone number'); + } + }); + + test('tests no errors are returned when phone number is valid', async () => { + mswServer.use( + phoneNumberValidations.mockValidDutchPhonenumberPost, + phoneNumberValidations.mockValidInternationalPhonenumberPost + ); + + const component = { + component: phoneNumberComponent, + options: { + baseUrl: BASE_URL, + }, + }; + + for (const sample of validSamples) { + const result = await pluginsAPIValidator.check(component, undefined, sample); + expect(result).toBe(true); + } + }); + + test('tests no errors are returned when phone number is null', async () => { + mswServer.use( + phoneNumberValidations.mockValidDutchPhonenumberPost, + phoneNumberValidations.mockValidInternationalPhonenumberPost + ); + + const component = { + component: phoneNumberComponent, + options: { + baseUrl: BASE_URL, + }, + }; + + const result = await pluginsAPIValidator.check(component, undefined, null); + expect(result).toBe(true); + }); +});