From a2db40966c32cdded4807a7d8ef3022ab3c0c8a8 Mon Sep 17 00:00:00 2001 From: Justin Shih Date: Tue, 7 Feb 2023 16:02:33 -0800 Subject: [PATCH] fix: change timestamp check in date validation function --- .../lib/__tests__/forms/validation.test.ts | 164 ++++++++------ .../lib/utils/forms/validation-helpers.ts | 205 ++++++++---------- .../lib/utils/forms/validation.ts | 15 +- 3 files changed, 198 insertions(+), 186 deletions(-) diff --git a/packages/codegen-ui-react/lib/__tests__/forms/validation.test.ts b/packages/codegen-ui-react/lib/__tests__/forms/validation.test.ts index ec6499ae6..9b93ec90b 100644 --- a/packages/codegen-ui-react/lib/__tests__/forms/validation.test.ts +++ b/packages/codegen-ui-react/lib/__tests__/forms/validation.test.ts @@ -14,7 +14,7 @@ limitations under the License. */ import { FieldValidationConfiguration, ValidationTypes } from '@aws-amplify/codegen-ui/lib/types/form/form-validation'; -import { validateField } from '../../utils/forms/validation'; +import { parseDateValidator, validateField } from '../../utils/forms/validation'; describe('validateField tests', () => { it('should validate REQUIRED type', () => { @@ -157,77 +157,105 @@ describe('validateField tests', () => { validateField(3, [{ type: ValidationTypes.EQUAL_TO_NUM, numValues: [4, 5, 6], validationMessage: 'test' }]), ).toEqual({ hasError: true, errorMessage: 'test' }); }); - it('should validate BE_AFTER type', () => { - const startDate = new Date().toDateString(); - const endDate1 = new Date('2021-01-09').toDateString(); - const endDate2 = new Date('3000-01-09').toDateString(); - expect( - validateField(startDate, [{ type: ValidationTypes.BE_AFTER, strValues: [endDate1], validationMessage: '' }]), - ).toEqual({ hasError: false }); - expect( - validateField(startDate, [{ type: ValidationTypes.BE_AFTER, strValues: [endDate2], validationMessage: '' }]), - ).toEqual({ hasError: true, errorMessage: `The value must be after ${endDate2}` }); - expect( - validateField(startDate, [{ type: ValidationTypes.BE_AFTER, strValues: [''], validationMessage: 'test' }]), - ).toEqual({ hasError: true, errorMessage: 'test' }); + describe('DateTime tests', () => { + it('should parse Date and timestamp', () => { + const time = Date.now().toString(); + const timestamp = parseDateValidator(time); + const invalidTimestamp = parseDateValidator('1232131asdfasf123'); + const ddMMYYYY = parseDateValidator('01 Jan 2022'); + const mmDDYYYY = parseDateValidator('1/01/2022'); + const yyyyMMDD = parseDateValidator('2022-1-1'); - const startTime = Date.now(); - const endTime1 = startTime - 10; - expect( - validateField(startTime, [ - { type: ValidationTypes.BE_AFTER, strValues: [endTime1.toString()], validationMessage: '' }, - ]), - ).toEqual({ hasError: false }); - expect( - validateField(endTime1, [ - { type: ValidationTypes.BE_AFTER, strValues: [startTime.toString()], validationMessage: '' }, - ]), - ).toEqual({ hasError: true, errorMessage: `The value must be after ${startTime}` }); - expect( - validateField(endTime1, [ - { type: ValidationTypes.BE_AFTER, strValues: [startTime.toString()], validationMessage: 'test' }, - ]), - ).toEqual({ hasError: true, errorMessage: 'test' }); - }); - it('should validate BE_BEFORE type', () => { - const startDate = new Date('2022-01-09').toString(); - const endDate1 = new Date('2023-01-09').toString(); - const endDate2 = new Date('2021-01-09').toString(); + expect(timestamp.toString()).toEqual(time); + expect(new Date(invalidTimestamp).toString()).toEqual('Invalid Date'); + expect(ddMMYYYY).toEqual('01 Jan 2022'); + expect(mmDDYYYY).toEqual('1/01/2022'); + expect(yyyyMMDD).toEqual('2022-1-1'); + }); + it('should validate BE_AFTER type', () => { + // timestamp tests + const startTime = Date.now(); + const endTime1 = startTime + 10; + const endTime2 = startTime - 10; - expect( - validateField(startDate, [ - { type: ValidationTypes.BE_BEFORE, strValues: [endDate1.toString()], validationMessage: '' }, - ]), - ).toEqual({ hasError: false }); - expect( - validateField(startDate, [ - { type: ValidationTypes.BE_BEFORE, strValues: [endDate2.toString()], validationMessage: '' }, - ]), - ).toEqual({ hasError: true, errorMessage: `The value must be before ${endDate2}` }); - expect( - validateField(startDate, [ - { type: ValidationTypes.BE_BEFORE, strValues: [endDate2.toString()], validationMessage: 'test' }, - ]), - ).toEqual({ hasError: true, errorMessage: 'test' }); + const dateFormatTestCases = [ + { + startDate: '01 Jan 2022', + endDate1: 'Jan 08 2023', + endDate2: '01 Jan 2021', + }, + { + startDate: '01/09/2022', + endDate1: '01/09/2023', + endDate2: '01/09/2021', + }, + { + startDate: '2022-1-9', + endDate1: '123123asdfdf123123', + endDate2: '2021-1-9', + }, + { + startDate: startTime, + endDate1: endTime1.toString(), + endDate2: endTime2.toString(), + }, + ]; - const startTime = Date.now(); - const endTime1 = startTime + 10; - expect( - validateField(startTime, [ - { type: ValidationTypes.BE_BEFORE, strValues: [endTime1.toString()], validationMessage: '' }, - ]), - ).toEqual({ hasError: false }); - expect( - validateField(endTime1, [ - { type: ValidationTypes.BE_BEFORE, strValues: [startTime.toString()], validationMessage: '' }, - ]), - ).toEqual({ hasError: true, errorMessage: `The value must be before ${startTime}` }); - expect( - validateField(endTime1, [ - { type: ValidationTypes.BE_BEFORE, strValues: [startTime.toString()], validationMessage: 'test' }, - ]), - ).toEqual({ hasError: true, errorMessage: 'test' }); + dateFormatTestCases.forEach(({ startDate, endDate1, endDate2 }) => { + expect( + validateField(startDate, [{ type: ValidationTypes.BE_AFTER, strValues: [endDate2], validationMessage: '' }]), + ).toEqual({ hasError: false }); + expect( + validateField(startDate, [{ type: ValidationTypes.BE_AFTER, strValues: [endDate1], validationMessage: '' }]), + ).toEqual({ hasError: true, errorMessage: `The value must be after ${endDate1}` }); + expect( + validateField(startDate, [{ type: ValidationTypes.BE_AFTER, strValues: [''], validationMessage: 'test' }]), + ).toEqual({ hasError: true, errorMessage: 'test' }); + }); + }); + + it('should validate BE_BEFORE type', () => { + // timestamp tests + const startTime = Date.now(); + const endTime1 = startTime + 10; + const endTime2 = startTime - 10; + + const dateFormatTestCases = [ + { + startDate: '01 Jan 2022', + endDate1: 'Jan 08 2023', + endDate2: '01 Jan 2021', + }, + { + startDate: '01/09/2022', + endDate1: '01/09/2023', + endDate2: '01/09/2021', + }, + { + startDate: '2022-01-09', + endDate1: '2023-01-09', + endDate2: '123123asdfdf123123', + }, + { + startDate: startTime, + endDate1: endTime1.toString(), + endDate2: endTime2.toString(), + }, + ]; + dateFormatTestCases.forEach(({ startDate, endDate1, endDate2 }) => { + expect( + validateField(startDate, [{ type: ValidationTypes.BE_BEFORE, strValues: [endDate1], validationMessage: '' }]), + ).toEqual({ hasError: false }); + expect( + validateField(startDate, [{ type: ValidationTypes.BE_BEFORE, strValues: [endDate2], validationMessage: '' }]), + ).toEqual({ hasError: true, errorMessage: `The value must be before ${endDate2}` }); + expect( + validateField(startDate, [{ type: ValidationTypes.BE_BEFORE, strValues: [''], validationMessage: 'test' }]), + ).toEqual({ hasError: true, errorMessage: 'test' }); + }); + }); }); + it('should validate EMAIL type', () => { expect(validateField('ab-cd@amazon.com', [{ type: ValidationTypes.EMAIL, validationMessage: '' }])).toEqual({ hasError: false, diff --git a/packages/codegen-ui-react/lib/utils/forms/validation-helpers.ts b/packages/codegen-ui-react/lib/utils/forms/validation-helpers.ts index 1b3cc35c0..6282bb262 100644 --- a/packages/codegen-ui-react/lib/utils/forms/validation-helpers.ts +++ b/packages/codegen-ui-react/lib/utils/forms/validation-helpers.ts @@ -266,6 +266,81 @@ export const generateValidateFieldFunction = () => ), ); +export const generateParseDateValidatorFunction = () => + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier('parseDateValidator'), + undefined, + undefined, + factory.createArrowFunction( + undefined, + undefined, + [ + factory.createParameterDeclaration( + undefined, + undefined, + undefined, + factory.createIdentifier('dateValidator'), + undefined, + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + undefined, + ), + ], + undefined, + factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + factory.createIdentifier('dateValue'), + undefined, + undefined, + factory.createNewExpression(factory.createIdentifier('Date'), undefined, [ + factory.createIdentifier('dateValidator'), + ]), + ), + ], + ts.NodeFlags.Const, + ), + ), + factory.createReturnStatement( + factory.createConditionalExpression( + factory.createBinaryExpression( + factory.createCallExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier('dateValue'), + factory.createIdentifier('toString'), + ), + undefined, + [], + ), + factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), + factory.createStringLiteral('Invalid Date'), + ), + factory.createToken(ts.SyntaxKind.QuestionToken), + factory.createCallExpression(factory.createIdentifier('parseInt'), undefined, [ + factory.createIdentifier('dateValidator'), + ]), + factory.createToken(ts.SyntaxKind.ColonToken), + factory.createIdentifier('dateValue'), + ), + ), + ], + true, + ), + ), + ), + ], + ts.NodeFlags.Const, + ), + ); + const numValuesSwitchStatement = () => factory.createSwitchStatement( factory.createPropertyAccessExpression(factory.createIdentifier('validation'), factory.createIdentifier('type')), @@ -902,61 +977,6 @@ const strValuesSwitchStatement = () => ), ]), factory.createCaseClause(factory.createStringLiteral('BeAfter'), [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier('afterTimeValue'), - undefined, - undefined, - factory.createCallExpression(factory.createIdentifier('parseInt'), undefined, [ - factory.createElementAccessExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('validation'), - factory.createIdentifier('strValues'), - ), - factory.createNumericLiteral('0'), - ), - ]), - ), - ], - ts.NodeFlags.Const, - ), - ), - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier('afterTimeValidator'), - undefined, - undefined, - factory.createConditionalExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('Number'), - factory.createIdentifier('isNaN'), - ), - undefined, - [factory.createIdentifier('afterTimeValue')], - ), - factory.createToken(ts.SyntaxKind.QuestionToken), - factory.createElementAccessExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('validation'), - factory.createIdentifier('strValues'), - ), - factory.createNumericLiteral('0'), - ), - factory.createToken(ts.SyntaxKind.ColonToken), - factory.createIdentifier('afterTimeValue'), - ), - ), - ], - ts.NodeFlags.Const, - ), - ), factory.createReturnStatement( factory.createObjectLiteralExpression( [ @@ -971,7 +991,15 @@ const strValuesSwitchStatement = () => ]), factory.createToken(ts.SyntaxKind.GreaterThanToken), factory.createNewExpression(factory.createIdentifier('Date'), undefined, [ - factory.createIdentifier('afterTimeValidator'), + factory.createCallExpression(factory.createIdentifier('parseDateValidator'), undefined, [ + factory.createElementAccessExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier('validation'), + factory.createIdentifier('strValues'), + ), + factory.createNumericLiteral('0'), + ), + ]), ]), ), ), @@ -1008,61 +1036,6 @@ const strValuesSwitchStatement = () => ), ]), factory.createCaseClause(factory.createStringLiteral('BeBefore'), [ - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier('beforeTimeValue'), - undefined, - undefined, - factory.createCallExpression(factory.createIdentifier('parseInt'), undefined, [ - factory.createElementAccessExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('validation'), - factory.createIdentifier('strValues'), - ), - factory.createNumericLiteral('0'), - ), - ]), - ), - ], - ts.NodeFlags.Const, - ), - ), - factory.createVariableStatement( - undefined, - factory.createVariableDeclarationList( - [ - factory.createVariableDeclaration( - factory.createIdentifier('beforeTimevalue'), - undefined, - undefined, - factory.createConditionalExpression( - factory.createCallExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('Number'), - factory.createIdentifier('isNaN'), - ), - undefined, - [factory.createIdentifier('beforeTimeValue')], - ), - factory.createToken(ts.SyntaxKind.QuestionToken), - factory.createElementAccessExpression( - factory.createPropertyAccessExpression( - factory.createIdentifier('validation'), - factory.createIdentifier('strValues'), - ), - factory.createNumericLiteral('0'), - ), - factory.createToken(ts.SyntaxKind.ColonToken), - factory.createIdentifier('beforeTimeValue'), - ), - ), - ], - ts.NodeFlags.Const, - ), - ), factory.createReturnStatement( factory.createObjectLiteralExpression( [ @@ -1077,7 +1050,15 @@ const strValuesSwitchStatement = () => ]), factory.createToken(ts.SyntaxKind.LessThanToken), factory.createNewExpression(factory.createIdentifier('Date'), undefined, [ - factory.createIdentifier('beforeTimevalue'), + factory.createCallExpression(factory.createIdentifier('parseDateValidator'), undefined, [ + factory.createElementAccessExpression( + factory.createPropertyAccessExpression( + factory.createIdentifier('validation'), + factory.createIdentifier('strValues'), + ), + factory.createNumericLiteral('0'), + ), + ]), ]), ), ), diff --git a/packages/codegen-ui-react/lib/utils/forms/validation.ts b/packages/codegen-ui-react/lib/utils/forms/validation.ts index c1eb43325..b33887fc9 100644 --- a/packages/codegen-ui-react/lib/utils/forms/validation.ts +++ b/packages/codegen-ui-react/lib/utils/forms/validation.ts @@ -2,6 +2,7 @@ import { fieldValidationConfigurationDeclaration, generateCheckValidationFunction, + generateParseDateValidatorFunction, generateValidateFieldFunction, validationResponseDeclaration, } from './validation-helpers'; @@ -38,6 +39,11 @@ export const validateField = (value: any, validations: FieldValidationConfigurat return { hasError: false }; }; +export const parseDateValidator = (dateValidator: string) => { + const isTimestamp = `${parseInt(dateValidator)}`.length === dateValidator.length; + return isTimestamp ? parseInt(dateValidator) : dateValidator; +}; + const checkValidation = (value: any, validation: FieldValidationConfiguration) => { if (validation.numValues?.length) { switch (validation.type) { @@ -94,17 +100,13 @@ const checkValidation = (value: any, validation: FieldValidationConfiguration) = errorMessage: validation.validationMessage || `The value must not contain ${validation.strValues.join(', ')}`, }; case 'BeAfter': - const afterTimeValue = parseInt(validation.strValues[0]); - const afterTimeValidator = Number.isNaN(afterTimeValue) ? validation.strValues[0] : afterTimeValue; return { - hasError: !(new Date(value) > new Date(afterTimeValidator)), + hasError: !(new Date(value) > new Date(parseDateValidator(validation.strValues[0]))), errorMessage: validation.validationMessage || `The value must be after ${validation.strValues[0]}`, }; case 'BeBefore': - const beforeTimeValue = parseInt(validation.strValues[0]); - const beforeTimevalue = Number.isNaN(beforeTimeValue) ? validation.strValues[0] : beforeTimeValue; return { - hasError: !(new Date(value) < new Date(beforeTimevalue)), + hasError: !(new Date(value) < new Date(parseDateValidator(validation.strValues[0]))), errorMessage: validation.validationMessage || `The value must be before ${validation.strValues[0]}`, }; } @@ -164,6 +166,7 @@ export const generateValidationFunction = () => { validationResponseDeclaration(), fieldValidationConfigurationDeclaration(), generateValidateFieldFunction(), + generateParseDateValidatorFunction(), generateCheckValidationFunction(), ]; };