diff --git a/lib/editor/util/validation.js b/lib/editor/util/validation.js index 43fa8006d..b34b3a7cc 100644 --- a/lib/editor/util/validation.js +++ b/lib/editor/util/validation.js @@ -15,6 +15,8 @@ export type EditorValidationIssue = { reason: string } +const EMPTY_FIELD_REASON = 'Required field must not be empty' + export function doesNotExist (value: any): boolean { return value === '' || value === null || typeof value === 'undefined' } @@ -39,7 +41,6 @@ export function validate ( const isOptionalAndEmpty = !required && valueDoesNotExist const agencies = getTableById(tableData, 'agency') let locationType: ?number = null - let reason = 'Required field must not be empty' // entity.locationtype is a string. Convert to number for conditinals later on. if (entity && entity.location_type !== null) { @@ -52,13 +53,21 @@ export function validate ( result: false | EditorValidationIssue } + /** + * Construct an EditorValidationIssue for the field name and reason (defaults to + * empty field message). + */ + function validationIssue (reason = EMPTY_FIELD_REASON, field = name) { + return {field, invalid: true, reason} + } + /** * Checks whether value is a positive number */ function checkPositiveNumber (): CheckPositiveOutput { if (isRequiredButEmpty) { return { - result: {field: name, invalid: isRequiredButEmpty, reason} + result: validationIssue() } } else if (isOptionalAndEmpty) { return { @@ -70,14 +79,14 @@ export function validate ( // make sure value is parseable to a number if (isNaN(num)) { return { - result: {field: name, invalid: true, reason: 'Field must be a valid number'} + result: validationIssue('Field must be a valid number') } } // make sure value is positive if (num < 0) { return { - result: {field: name, invalid: true, reason: 'Field must be a positive number'} + result: validationIssue('Field must be a positive number') } } @@ -112,21 +121,20 @@ export function validate ( idList.length > 1 && valueDoesNotExist ) { - reason = 'Identifier is required if more than one agency exists' - return {field: name, invalid: isRequiredButEmpty, reason} + return validationIssue('Identifier is required if more than one agency exists') } if (isRequiredButEmpty || isNotUnique) { + let reason if (isNotUnique) { reason = 'Identifier must be unique' } - return {field: name, invalid: isRequiredButEmpty || isNotUnique, reason} + return validationIssue(reason) } else { return false } case 'TEXT': if (name === 'stop_name' && locationType !== null && (typeof locationType === 'number' && locationType <= 2)) { - reason = 'Stop name is required for your current location type' - return {field: name, invalid: isOptionalAndEmpty, reason} + return validationIssue('Stop name is required for stop, station, and entrance location types.') } if (name === 'route_short_name' && !value && entity && entity.route_long_name) { return false @@ -139,7 +147,7 @@ export function validate ( return false } else { if (isRequiredButEmpty) { - return {field: name, invalid: isRequiredButEmpty, reason} + return validationIssue() } else { return false } @@ -150,82 +158,77 @@ export function validate ( case 'GTFS_FARE': case 'GTFS_SERVICE': if (isRequiredButEmpty) { - return {field: name, invalid: isRequiredButEmpty, reason} + return validationIssue() } else { return false } case 'URL': const isNotUrl = value && !validator.isURL(value) if (isRequiredButEmpty || isNotUrl) { + let reason if (isNotUrl) { reason = 'Field must contain valid URL.' } - return {field: name, invalid: isRequiredButEmpty || isNotUrl, reason} + return validationIssue(reason) } else { return false } case 'EMAIL': const isNotEmail = value && !validator.isEmail(value) if (isRequiredButEmpty || isNotEmail) { + let reason if (isNotEmail) { reason = 'Field must contain valid email address.' } - return {field: name, invalid: isRequiredButEmpty || isNotEmail, reason} + return validationIssue(reason) } else { return false } case 'GTFS_ZONE': if (isRequiredButEmpty) { - return {field: name, invalid: isRequiredButEmpty, reason} + return validationIssue() } else { return false } case 'TIMEZONE': if (isRequiredButEmpty) { - return {field: name, invalid: isRequiredButEmpty, reason} + return validationIssue() } else { return false } case 'LANGUAGE': if (isRequiredButEmpty) { - return {field: name, invalid: isRequiredButEmpty, reason} + return validationIssue() } else { return false } case 'LATITUDE': const isNotLat = value > 90 || value < -90 if (isNotLat) { - reason = 'Field must be valid latitude.' - return {field: name, invalid: isNotLat, reason} + return validationIssue('Field must be valid latitude.') } if (isOptionalAndEmpty && locationType !== null && (typeof locationType === 'number' && locationType <= 2)) { - reason = 'Latitude and Longitude are required for your current location type' - return {field: name, invalid: isOptionalAndEmpty, reason} + return validationIssue('Latitude and Longitude are required for your current location type') } return false case 'LONGITUDE': const isNotLng = value > 180 || value < -180 if (isNotLng) { - reason = 'Field must be valid longitude.' - return {field: name, invalid: isOptionalAndEmpty || isNotLng, reason} + return validationIssue('Field must be valid longitude.') } - if (isOptionalAndEmpty && locationType !== null && (typeof locationType === 'number' && locationType <= 2)) { - reason = 'Latitude and Longitude are required for your current location type' - return {field: name, invalid: isOptionalAndEmpty || isNotLng, reason} + if (isOptionalAndEmpty && typeof locationType === 'number' && locationType <= 2) { + return validationIssue('Latitude and Longitude are required for your current location type') } return false case 'TIME': case 'NUMBER': const isNotANumber = isNaN(value) if (isRequiredButEmpty || isNotANumber) { + let reason if (isNotANumber) { reason = 'Field must be valid number' } - return { - field: name, - invalid: isRequiredButEmpty || isNotANumber, - reason - } + return validationIssue(reason) } else { return false } @@ -247,8 +250,7 @@ export function validate ( } if (!hasService && name === 'monday') { // only add validation issue for one day of week (monday) - reason = 'Calendar must have service for at least one day' - return {field: name, invalid: isRequiredButEmpty, reason} + return validationIssue('Calendar must have service for at least one day') } return false case 'DROPDOWN': @@ -257,7 +259,7 @@ export function validate ( field.options && field.options.findIndex(o => o.value === '') === -1 ) { - return {field: name, invalid: isRequiredButEmpty, reason} + return validationIssue() } else { return false } @@ -267,11 +269,7 @@ export function validate ( agencies.length > 1 ) { if (valueDoesNotExist) { - return { - field: name, - invalid: true, - reason: 'Field must be populated for feeds with more than one agency.' - } + return validationIssue('Field must be populated for feeds with more than one agency.') } } return false @@ -300,18 +298,19 @@ export function validate ( } } if (!value || value.length === 0) { - return {field: `dates`, invalid: true, reason} + return validationIssue(EMPTY_FIELD_REASON, 'dates') } // check if date already exists in this or other exceptions for (let i = 0; i < value.length; i++) { + const dateItemName = `dates-${i}` if (dateMap[value[i]] && dateMap[value[i]].length > 1) { // eslint-disable-next-line standard/computed-property-even-spacing - reason = `Date (${value[ + const reason = `Date (${value[ i ]}) cannot appear more than once for all exceptions` - return {field: `dates-${i}`, invalid: true, reason} + return validationIssue(reason, dateItemName) } else if (!moment(value[i], 'YYYYMMDD', true).isValid()) { - return {field: `dates-${i}`, invalid: true, reason} + return validationIssue(EMPTY_FIELD_REASON, dateItemName) } } return false @@ -331,7 +330,7 @@ export function validate ( ) ) ) { - return {field: name, invalid: true, reason: 'Field must be a positive integer'} + return validationIssue('Field must be a positive integer') } return false case 'POSITIVE_NUM': @@ -342,7 +341,7 @@ export function validate ( case 'COLOR': default: if (isRequiredButEmpty) { - return {field: name, invalid: isRequiredButEmpty, reason} + return validationIssue() } return false }