import sinon from 'sinon'; import { JsonValue } from 'type-fest'; import { expect } from '../../../test-utils'; import { EStepType, generateSchemaFromJoi, getJoiDescribeFromCode, getJoiSchemaFromCode, generatePayload, } from '../../helpers/generate'; import { generate, getValidStepType, replaceCustomJoiDateOfBirth } from '../generate'; import { CLIError, ExitCodes } from '../../errors/platform-error'; import { ESchemaComponentType } from '../../helpers/generate/joi-types-helpers'; import * as apidocsPromptExports from '../apidocs-prompt'; import * as readAuthAndConfigExports from '../../helpers/read-auth-and-config'; import * as checkValidDirectoryExports from '../../helpers/check-valid-product-module-directory'; import * as readDefinitionExports from '../../helpers/read-product-module-definition'; import * as stubData from './stub-data'; describe('rp generate', function () { const sandbox = sinon.createSandbox(); it('fails with a friendly CLIError when called without any options', async function () { let thrown: unknown; try { await generate({}); } catch (error) { thrown = error; } expect(thrown).to.be.instanceOf(CLIError); expect((thrown as CLIError).exitCode).to.equal(ExitCodes.GENERAL_ERROR); expect((thrown as CLIError).message).to.contain('rp generate must be called with one or more options'); expect((thrown as CLIError).message).to.contain('rp generate --help'); }); it('--api-docs | delegates to the API docs prompt flow without reading the module definition', async function () { sandbox.stub(readAuthAndConfigExports, 'readAuthAndConfig').returns(stubData.productModuleAuthAndConfig); sandbox.stub(checkValidDirectoryExports, 'checkValidProductModuleDirectory'); const readDefinitionStub = sandbox.stub(readDefinitionExports, 'readProductModuleDefinition'); const promptStub = sandbox.stub(apidocsPromptExports, 'generateApiDocsPrompt').resolves(); await generate({ apiDocs: true }); sandbox.assert.calledOnceWithExactly(promptStub, { apiKey: stubData.productModuleAuthAndConfig.apiKey, productModuleConfig: stubData.productModuleAuthAndConfig.config, }); sandbox.assert.notCalled(readDefinitionStub); }); it('should replace any use of custom joi type .dateOfBirth() with .date() when ingesting local product module code.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ date_of_birth: Joi.dateOfBirth().required(), date_of_purchase: Joi.date() .iso() .max(moment().format('YYYY-MM-DD')) .min(moment().subtract(60, 'days').format('YYYY-MM-DD')) .required(), }) const age = getAgeFromDateOfBirth(child.date_of_birth); `; const filteredModuleCode = replaceCustomJoiDateOfBirth(moduleCode); expect(filteredModuleCode).to.not.contain('.dateOfBirth()'); expect(filteredModuleCode).to.contain('const age = getAgeFromDateOfBirth(child.date_of_birth);'); }); it('--workflow alterations | should generate all schemas components for existing alterations.', function () { const moduleCode = ` const updateCoveredLifeSchema = Joi.object() .keys({ first_name: Joi.string().required(), }); const removeCoveredLifeSchema = Joi.object() .keys({ first_name: Joi.string().required(), }); const addCoveredItemSchema = Joi.object() .keys({ first_name: Joi.string().required(), }); `; const schemaType = EStepType.Alterations; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [ { key: 'update_covered_life', name: 'Fancy name', schema: [], }, { key: 'remove_covered_life', name: 'Another fancy name', schema: [], }, { key: 'add_covered_item', name: 'Some other fancy name', schema: [], }, ], schemaType, }); expect(joiDescribeMap.size).to.equals(3); const updateCoveredLifeKeys = Object.keys(joiDescribeMap.get('update-covered-life')?.keys); expect(updateCoveredLifeKeys.length).to.equal(1); expect(updateCoveredLifeKeys[0]).to.equal('first_name'); const removeCoveredLifeKeys = Object.keys(joiDescribeMap.get('remove-covered-life')?.keys); expect(removeCoveredLifeKeys.length).to.equal(1); expect(removeCoveredLifeKeys[0]).to.equal('first_name'); expect(joiDescribeMap.get('add-covered-item')).to.not.equal(undefined); const addCoveredItemKeys = Object.keys(joiDescribeMap.get('add-covered-item')?.keys); expect(addCoveredItemKeys.length).to.equal(1); expect(addCoveredItemKeys[0]).to.equal('first_name'); }); it('--workflow quote | should correctly generate currency and currency-slider schema components for number parameters.', function () { const quoteJoiSchema = ` const quoteSchema = Joi.object() .keys({ purchase_amount: Joi.number().meta({ root_type: 'currency' }).required(), income: Joi.number().min(1).max(1000000).meta({ root_type: 'currency-slider', prefix: 'ZAR', increment: '100000' }).required(), }); `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: quoteJoiSchema, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const requiredValidators = [ { validation: { type: 'required', }, }, ]; expect(quoteSchema.length).to.equals(2); expect(quoteSchema[0].type).to.equals(ESchemaComponentType.Currency); expect(quoteSchema[0].key).to.equals('purchase_amount'); expect(quoteSchema[0].label).to.equals('Purchase amount'); expect(quoteSchema[0].validators).to.deep.equal(requiredValidators); const minMaxValidators = [ { validation: { min: 1, type: 'greaterThanCurrency', }, }, { validation: { max: 1_000_000, type: 'lessThanCurrency', }, }, ...requiredValidators, ]; expect(quoteSchema[1].type).to.equals(ESchemaComponentType.CurrencySlider); expect(quoteSchema[1].key).to.equals('income'); expect(quoteSchema[1].label).to.equals('Income'); expect(quoteSchema[1].validators).to.deep.equal(minMaxValidators); const expectedProps = { prefix: 'ZAR', increment: '100000', }; expect(quoteSchema[1].props).to.deep.equal(expectedProps); }); it('--workflow quote | should correctly generate number-slider schema components for number parameters.', function () { const quoteJoiSchema = ` const quoteSchema = Joi.object() .keys({ income: Joi.number().min(1).max(1000000).meta({ root_type: 'number-slider', increment: '100000' }).required(), }); `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: quoteJoiSchema, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const validators = [ { validation: { min: 1, type: 'greaterThanNumber', }, }, { validation: { max: 1_000_000, type: 'lessThanNumber', }, }, { validation: { type: 'required', }, }, ]; expect(quoteSchema.length).to.equals(1); expect(quoteSchema[0].type).to.equals(ESchemaComponentType.NumberSlider); expect(quoteSchema[0].key).to.equals('income'); expect(quoteSchema[0].label).to.equals('Income'); expect(quoteSchema[0].validators).to.deep.equal(validators); const expectedProps = { increment: '100000', }; expect(quoteSchema[0].props).to.deep.equal(expectedProps); }); it('--workflow quote | should correctly generate text schema components for string parameters.', function () { const quoteJoiSchema = ` const quoteSchema = Joi.object() .keys({ first_name: Joi.string().required(), last_name: Joi.string().required(), }); `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: quoteJoiSchema, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const requiredValidators = [ { validation: { type: 'required', }, }, ]; expect(quoteSchema.length).to.equals(2); expect(quoteSchema[0].type).to.equals(ESchemaComponentType.Text); expect(quoteSchema[0].key).to.equals('first_name'); expect(quoteSchema[0].label).to.equals('First name'); expect(quoteSchema[0].validators).to.deep.equal(requiredValidators); expect(quoteSchema[1].type).to.equals(ESchemaComponentType.Text); expect(quoteSchema[1].key).to.equals('last_name'); expect(quoteSchema[1].label).to.equals('Last name'); expect(quoteSchema[1].validators).to.deep.equal(requiredValidators); }); it('--workflow quote | should correctly generate country schema components for string parameters.', function () { const quoteJoiSchema = ` const quoteSchema = Joi.object() .keys({ country_of_birth: Joi.string().meta({ root_type: 'country' }).required() }); `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: quoteJoiSchema, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const requiredValidators = [ { validation: { type: 'required', }, }, ]; expect(quoteSchema.length).to.equals(1); expect(quoteSchema[0].type).to.equals(ESchemaComponentType.Country); expect(quoteSchema[0].key).to.equals('country_of_birth'); expect(quoteSchema[0].label).to.equals('Country of birth'); expect(quoteSchema[0].validators).to.deep.equal(requiredValidators); }); it('--workflow quote | should correctly generate cellphone schema components for string parameters.', function () { const quoteJoiSchema = ` const quoteSchema = Joi.object() .keys({ cell: Joi.string().meta({ root_type: 'cellphone' }).required() }); `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: quoteJoiSchema, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const requiredValidators = [ { validation: { type: 'required', }, }, ]; expect(quoteSchema.length).to.equals(1); expect(quoteSchema[0].type).to.equals(ESchemaComponentType.Cellphone); expect(quoteSchema[0].key).to.equals('cell'); expect(quoteSchema[0].label).to.equals('Cell'); expect(quoteSchema[0].validators).to.deep.equal(requiredValidators); }); it('--workflow quote | should correctly generate number schema components for number parameters.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ cover_amount: Joi.number() .integer() .min(10000000) .max(200000000) }); `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const coverAmountLimitValidators = [ { validation: { min: 10_000_000, type: 'greaterThanNumber', }, }, { validation: { max: 200_000_000, type: 'lessThanNumber', }, }, ]; expect(quoteSchema.length).to.equals(1); expect(quoteSchema[0].validators).to.deep.equal(coverAmountLimitValidators); }); it('--workflow quote | should correctly generate select schema components for string parameters.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ smoking_status: Joi.string().valid('smoker', 'non_smoker').required() }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const options = [ { label: 'Smoker', value: 'smoker', }, { label: 'Non Smoker', value: 'non_smoker', }, ]; expect(quoteSchema.length).to.equals(1); expect(quoteSchema[0].type).to.equal(ESchemaComponentType.Select); expect(quoteSchema[0].label).to.equal('Smoking status'); expect(quoteSchema[0].options).to.deep.equal(options); }); it('--workflow quote | should correctly generate select schema components for number parameters.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ pet_age: Joi.number().valid(1, 2, 3, 4) }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const options = [ { label: '1', value: 1, }, { label: '2', value: 2, }, { label: '3', value: 3, }, { label: '4', value: 4, }, ]; expect(quoteSchema.length).to.equals(1); expect(quoteSchema[0].type).to.equal(ESchemaComponentType.Select); expect(quoteSchema[0].label).to.equal('Pet age'); expect(quoteSchema[0].options).to.deep.equal(options); }); it('--workflow quote | should correctly generate radio schema components for string parameters.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ smoking_status: Joi.string().valid('smoker', 'non_smoker').meta({ root_type: 'radio-button' }).required(), are_you_the_registered_owner: Joi.string().valid('yes', 'no').meta({ root_type: 'radio' }).required() }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const smokerOptions = [ { label: 'Smoker', value: 'smoker', }, { label: 'Non Smoker', value: 'non_smoker', }, ]; const registeredOwnerOptions = [ { label: 'Yes', value: 'yes', }, { label: 'No', value: 'no', }, ]; expect(quoteSchema.length).to.equals(2); expect(quoteSchema[0].type).to.equal(ESchemaComponentType.RadioButton); expect(quoteSchema[0].label).to.equal('Smoking status'); expect(quoteSchema[0].options).to.deep.equal(smokerOptions); expect(quoteSchema[1].type).to.equal(ESchemaComponentType.Radio); expect(quoteSchema[1].label).to.equal('Are you the registered owner'); expect(quoteSchema[1].options).to.deep.equal(registeredOwnerOptions); }); it('--workflow quote | should correctly generate radio schema components for string parameters.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ additional_benefits: Joi.string().valid('theft', 'damage', 'accidental_loss').meta({ root_type: 'multiple-checkbox' }).required() }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const additionalBenefitsOptions = [ { label: 'Theft', value: 'theft', }, { label: 'Damage', value: 'damage', }, { label: 'Accidental Loss', value: 'accidental_loss', }, ]; expect(quoteSchema.length).to.equals(1); expect(quoteSchema[0].type).to.equal(ESchemaComponentType.MultipleCheckbox); expect(quoteSchema[0].label).to.equal('Additional benefits'); expect(quoteSchema[0].options).to.deep.equal(additionalBenefitsOptions); }); it('--workflow quote | should correctly generate checkbox schema components for boolean parameters.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ gift_purchase: Joi.boolean().required(), gift_purchase_with_default: Joi.boolean().required().default(false) }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); expect(quoteSchema.length).to.equals(2); expect(quoteSchema[0].type).to.equal(ESchemaComponentType.Checkbox); expect(quoteSchema[0].label).to.equal('Gift purchase'); expect(quoteSchema[0]).to.not.have.property('defaultValue'); expect(quoteSchema[1].type).to.equal(ESchemaComponentType.Checkbox); expect(quoteSchema[1].label).to.equal('Gift purchase with default'); expect(quoteSchema[1]).to.have.property('defaultValue'); expect(quoteSchema[1].defaultValue).to.equal(false); }); it('--workflow quote | should correctly generate date-picker schema components for date parameters.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ date_of_birth: Joi.date().iso().required(), date_of_purchase: Joi.date() .iso() .max(moment().format('YYYY-MM-DD')) .min(moment().subtract(60, 'days').format('YYYY-MM-DD')) .required(), }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); expect(quoteSchema.length).to.equals(2); expect(quoteSchema[0].type).to.equal(ESchemaComponentType.DatePicker); expect(quoteSchema[0].label).to.equal('Date of birth'); expect(quoteSchema[1].type).to.equal(ESchemaComponentType.DatePicker); expect(quoteSchema[1].label).to.equal('Date of purchase'); }); it('--workflow quote | should correctly generate nested object schema components for object parameters.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ partner: Joi.object().keys({ name: Joi.string().min(1).max(16).required(), birthdate: Joi.date().iso().required(), }).required(), }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const partnerSurnameExpectedComponent = { key: 'partner.name', type: 'text', label: 'Name', outputPath: 'partner.name', }; const partnerDateOfBirthExpectedComponent = { key: 'partner.birthdate', type: 'date-picker', label: 'Birthdate', outputPath: 'partner.birthdate', }; expect(quoteSchema.length).to.equals(2); expect(quoteSchema[0].key).to.equal(partnerSurnameExpectedComponent.key); expect(quoteSchema[0].type).to.equal(partnerSurnameExpectedComponent.type); expect(quoteSchema[0].label).to.equal(partnerSurnameExpectedComponent.label); expect(quoteSchema[0].outputPath).to.equal(partnerSurnameExpectedComponent.outputPath); expect(quoteSchema[1].key).to.equal(partnerDateOfBirthExpectedComponent.key); expect(quoteSchema[1].type).to.equal(partnerDateOfBirthExpectedComponent.type); expect(quoteSchema[1].label).to.equal(partnerDateOfBirthExpectedComponent.label); expect(quoteSchema[1].outputPath).to.equal(partnerDateOfBirthExpectedComponent.outputPath); const partnerSurnameRequiredValidators = quoteSchema[0].validators?.filter( (validator) => validator.validation.type === 'required', ); expect(partnerSurnameRequiredValidators?.length).to.equal(1); const partnerDateOfBirthRequiredValidators = quoteSchema[1].validators?.filter( (validator) => validator.validation.type === 'required', ); expect(partnerDateOfBirthRequiredValidators?.length).to.equal(1); }); it('--workflow quote | nested object schema components - required object with NOT required field validators should NOT have required validator.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ partner: Joi.object().keys({ surname: Joi.string().min(1).max(16), }).required(), }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); expect(quoteSchema.length).to.equals(1); const partnerSurnameRequiredvalidators = quoteSchema[0].validators?.filter( (validator) => validator.validation.type === 'required', ); expect(partnerSurnameRequiredvalidators?.length).to.equal(0); }); it('--workflow quote | nested object schema components - NOT required object with required field validators should NOT have required validator.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ partner: Joi.object().keys({ surname: Joi.string().min(1).max(16).required(), }), }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); expect(quoteSchema.length).to.equals(1); const partnerSurnameRequiredvalidators = quoteSchema[0].validators?.filter( (validator) => validator.validation.type === 'required', ); expect(partnerSurnameRequiredvalidators?.length).to.equal(0); }); it('--workflow quote | should correctly generate display condition for a joi-when-field.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ partner_included: Joi.boolean().required(), partner_name: Joi.when('partner_included',{ is: true, then: Joi.string().min(1).max(25).required(), otherwise: Joi.forbidden().allow(null) }).required(), country: Joi.when('partner_name',{ is: 'miss-africa', then: Joi.forbidden(), otherwise: Joi.string().required(), }).required(), province: Joi.when('country',{ is: 'south africa', then: Joi.string().required(), }).required(), street_number: Joi.when('province',{ is: 'western cape', then: Joi.number().min(1).max(25).required(), otherwise: Joi.allow(null) }).required(), }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const partnerNameDisplayConditions = [ { path: 'partner_included', value: true, condition: '===', }, ]; const partnerNameValidators = [ { validation: { min: 1, type: 'greaterThanLength', }, }, { validation: { max: 25, type: 'lessThanLength', }, }, { validation: { type: 'required', }, }, ]; const requiredValidator = [ { validation: { type: 'required', }, }, ]; /* partner_name: then: Joi.string(), otherwise: Joi.forbidden().allow(null) */ expect(quoteSchema[1].type).to.equal(ESchemaComponentType.Text); expect(quoteSchema[1].key).to.equal('partner_name_then'); expect(quoteSchema[1].label).to.equal('Partner name'); expect(quoteSchema[1].outputPath).to.equal('partner_name'); expect(quoteSchema[1].validators).to.deep.equal(partnerNameValidators); expect(quoteSchema[1].displayConditions).to.deep.equal(partnerNameDisplayConditions); const countryDisplayConditions = [ { path: 'partner_name', value: 'miss-africa', condition: '!==', }, ]; /* country: then: Joi.forbidden(), otherwise: Joi.string() */ expect(quoteSchema[2].type).to.equal(ESchemaComponentType.Text); expect(quoteSchema[2].key).to.equal('country_otherwise'); expect(quoteSchema[2].label).to.equal('Country'); expect(quoteSchema[2].outputPath).to.equal('country'); expect(quoteSchema[2].validators).to.deep.equal(requiredValidator); expect(quoteSchema[2].displayConditions).to.deep.equal(countryDisplayConditions); const proviceDisplayConditions = [ { path: 'country', value: 'south africa', condition: '===', }, ]; /* province: then: Joi.string(), otherwise: undefined */ expect(quoteSchema[3].type).to.equal(ESchemaComponentType.Text); expect(quoteSchema[3].key).to.equal('province_then'); expect(quoteSchema[3].label).to.equal('Province'); expect(quoteSchema[3].outputPath).to.equal('province'); expect(quoteSchema[3].validators).to.deep.equal(requiredValidator); expect(quoteSchema[3].displayConditions).to.deep.equal(proviceDisplayConditions); const streetNumberDisplayConditions = [ { path: 'province', value: 'western cape', condition: '===', }, ]; const streetNumberValidators = [ { validation: { min: 1, type: 'greaterThanNumber', }, }, { validation: { max: 25, type: 'lessThanNumber', }, }, ...requiredValidator, ]; /* street_number: then: Joi.number(), otherwise: undefined */ expect(quoteSchema[4].type).to.equal(ESchemaComponentType.Number); expect(quoteSchema[4].key).to.equal('street_number_then'); expect(quoteSchema[4].label).to.equal('Street number'); expect(quoteSchema[4].outputPath).to.equal('street_number'); expect(quoteSchema[4].validators).to.deep.equal(streetNumberValidators); expect(quoteSchema[4].displayConditions).to.deep.equal(streetNumberDisplayConditions); }); it('--workflow quote | should correctly generate nested object schema components from joi-when-field.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ partner_included: Joi.boolean().required(), partner: Joi.when('partner_included', { is: true, then: Joi.object().keys({ surname: Joi.string().min(1).max(16).required(), date: Joi.date().iso().required(), }).required(), otherwise: Joi.forbidden().allow(null) }), }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const partnerSurnameExpectedComponent = { key: 'partner.surname_then', type: 'text', label: 'Surname', outputPath: 'partner.surname', }; const partnerDateOfBirthExpectedComponent = { key: 'partner.date_then', type: 'date-picker', label: 'Date', outputPath: 'partner.date', }; expect(quoteSchema.length).to.equals(3); expect(quoteSchema[1].key).to.equal(partnerSurnameExpectedComponent.key); expect(quoteSchema[1].type).to.equal(partnerSurnameExpectedComponent.type); expect(quoteSchema[1].label).to.equal(partnerSurnameExpectedComponent.label); expect(quoteSchema[1].outputPath).to.equal(partnerSurnameExpectedComponent.outputPath); expect(quoteSchema[2].key).to.equal(partnerDateOfBirthExpectedComponent.key); expect(quoteSchema[2].type).to.equal(partnerDateOfBirthExpectedComponent.type); expect(quoteSchema[2].label).to.equal(partnerDateOfBirthExpectedComponent.label); expect(quoteSchema[2].outputPath).to.equal(partnerDateOfBirthExpectedComponent.outputPath); }); it('--workflow quote | should correctly generate array schema components from joi.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ wishlist: Joi.array() .min(1) .max(30) .items( Joi.object().keys({ item_value: Joi.number().integer().min(100).max(5000000).required(), item_description: Joi.string().required(), }), ).required() }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const itemsSchema = { type: 'list', label: 'Wishlist', props: { addButtonLabel: 'Add', }, minNumber: 1, maxNumber: 30, validators: [ { validation: { type: 'required', }, }, ], components: [ { key: 'item_value', type: 'number', label: 'Item value', validators: [ { validation: { min: 100, type: 'greaterThanNumber', }, }, { validation: { max: 5_000_000, type: 'lessThanNumber', }, }, { validation: { type: 'required', }, }, ], }, { key: 'item_description', type: 'text', label: 'Item description', validators: [ { validation: { type: 'required', }, }, ], }, ], outputPathList: 'wishlist', }; expect(quoteSchema.length).to.equals(1); expect(quoteSchema[0]).to.deep.equal(itemsSchema); }); it('--workflow quote | should correctly generate conditional display conditions.', function () { const moduleCode = ` const quoteSchema = Joi.object() .keys({ country: Joi.string().required(), postal_code: Joi.when('country',{ is: 'London', then: Joi.string().required(), otherwise: Joi.number().required() }).required(), }) `; const schemaType = EStepType.Quote; const joiDescribeMap = getJoiDescribeFromCode({ localDefinitionCode: moduleCode, alterationHooks: [], schemaType, }); const joiDescribe = joiDescribeMap.get(schemaType) ?? {}; const quoteSchema = generateSchemaFromJoi({ joiDescribe, }); const expectedSchema = [ { key: 'country', type: 'text', label: 'Country', validators: [ { validation: { type: 'required', }, }, ], outputPath: 'country', }, { key: 'postal_code_then', type: 'text', label: 'Postal code', validators: [ { validation: { type: 'required', }, }, ], outputPath: 'postal_code', displayConditions: [ { path: 'country', value: 'London', condition: '===', }, ], }, { key: 'postal_code_otherwise', type: 'number', label: 'Postal code', validators: [ { validation: { type: 'required', }, }, ], outputPath: 'postal_code', displayConditions: [ { path: 'country', value: 'London', condition: '!==', }, ], }, ]; expect(quoteSchema.length).to.equals(3); expect(quoteSchema).to.deep.equal(expectedSchema); }); it('--payloads quote | should generate a valid quote payload', function () { const quoteJoiSchema = ` const quoteSchema = Joi.object() .keys({ first_name: Joi.string().required(), last_name: Joi.string().required(), }); `; const schemaType = EStepType.Quote; const joiSchemaMap = getJoiSchemaFromCode({ localDefinitionCode: quoteJoiSchema, alterationHooks: [], schemaType, }); const quotePayload: JsonValue = generatePayload({ joiSchema: joiSchemaMap.get(schemaType), }); expect(quotePayload).to.be.an('object'); expect(quotePayload).to.have.property('first_name'); expect(quotePayload).to.have.property('last_name'); }); it('--payloads application |should correctly generate a valid application payload', function () { const applicationJoiSchema = ` const applicationSchema = Joi.object() .keys({ first_name: Joi.string().required(), last_name: Joi.string().required(), }); `; const schemaType = EStepType.Application; const joiSchemaMap = getJoiSchemaFromCode({ localDefinitionCode: applicationJoiSchema, alterationHooks: [], schemaType, }); const quotePayload: JsonValue = generatePayload({ joiSchema: joiSchemaMap.get(schemaType), }); expect(quotePayload).to.be.an('object'); expect(quotePayload).to.have.property('first_name'); expect(quotePayload).to.have.property('last_name'); }); it('--payloads quot | should throw an error if an incorrect command option is selected ', function () { expect(() => getValidStepType({ step: 'quot' })).to.throw("Unsupported option: 'quot'"); }); afterEach(async function () { sandbox.verifyAndRestore(); }); });