From 628b4c7d77cd51d2007077fa3ac052fbfae4db53 Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Mon, 20 Nov 2023 12:40:22 +0530 Subject: [PATCH] feat: add support for comparing nested values in sameAs and notSameAs rules Closes: #20 --- package.json | 2 + src/schema/string/rules.ts | 25 +++++++--- tests/unit/rules/string.spec.ts | 81 +++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index b6546ad..ff4f3a6 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@japa/expect-type": "^2.0.0", "@japa/runner": "^3.0.5", "@swc/core": "^1.3.96", + "@types/dlv": "^1.1.4", "@types/node": "^20.9.2", "benchmark": "^2.1.4", "c8": "^8.0.1", @@ -63,6 +64,7 @@ "@types/validator": "^13.11.6", "@vinejs/compiler": "^2.3.0", "camelcase": "^8.0.0", + "dlv": "^1.1.3", "normalize-url": "^8.0.0", "validator": "^13.11.0" }, diff --git a/src/schema/string/rules.ts b/src/schema/string/rules.ts index c3936a4..4480c07 100644 --- a/src/schema/string/rules.ts +++ b/src/schema/string/rules.ts @@ -7,12 +7,15 @@ * file that was distributed with this source code. */ -import normalizeEmail from 'validator/lib/normalizeEmail.js' +import delve from 'dlv' +import camelcase from 'camelcase' +import normalizeUrl from 'normalize-url' import escape from 'validator/lib/escape.js' import type { FieldContext } from '@vinejs/compiler/types' +import normalizeEmail from 'validator/lib/normalizeEmail.js' -import { helpers } from '../../vine/helpers.js' import { messages } from '../../defaults.js' +import { helpers } from '../../vine/helpers.js' import { createRule } from '../../vine/create_rule.js' import type { URLOptions, @@ -26,8 +29,18 @@ import type { AlphaNumericOptions, NormalizeEmailOptions, } from '../../types.js' -import camelcase from 'camelcase' -import normalizeUrl from 'normalize-url' + +/** + * Returns the nested value from the field root + * object or the sibling value from the field + * parent object + */ +function getNestedValue(key: string, field: FieldContext) { + if (key.indexOf('.') > -1) { + return delve(field.data, key) + } + return field.parent[key] +} /** * Validates the value to be a string @@ -280,7 +293,7 @@ export const sameAsRule = createRule<{ otherField: string }>((value, options, fi return } - const input = field.parent[options.otherField] + const input = getNestedValue(options.otherField, field) /** * Performing validation and reporting error @@ -302,7 +315,7 @@ export const notSameAsRule = createRule<{ otherField: string }>((value, options, return } - const input = field.parent[options.otherField] + const input = getNestedValue(options.otherField, field) /** * Performing validation and reporting error diff --git a/tests/unit/rules/string.spec.ts b/tests/unit/rules/string.spec.ts index 101d295..8913641 100644 --- a/tests/unit/rules/string.spec.ts +++ b/tests/unit/rules/string.spec.ts @@ -766,6 +766,44 @@ test.group('String | sameAs', () => { }, }, }, + { + errorsCount: 1, + rule: sameAsRule({ otherField: 'old.password' }), + value: 'foo', + field: { + name: 'password_confirmation', + parent: {}, + }, + error: 'The password_confirmation field and old.password field must be the same', + }, + { + errorsCount: 1, + rule: sameAsRule({ otherField: 'old.password' }), + value: 'foo', + field: { + name: 'password_confirmation', + parent: {}, + data: { + old: { + password: 'bar', + }, + }, + }, + error: 'The password_confirmation field and old.password field must be the same', + }, + { + rule: sameAsRule({ otherField: 'old.password' }), + value: 'foo', + field: { + name: 'password_confirmation', + parent: {}, + data: { + old: { + password: 'foo', + }, + }, + }, + }, ]) .run(stringRuleValidator) }) @@ -825,6 +863,49 @@ test.group('String | notSameAs', () => { }, }, }, + { + errorsCount: 1, + rule: notSameAsRule({ otherField: 'old.password' }), + value: 'foo', + field: { + name: 'password_confirmation', + parent: { + password: '', + }, + data: { + old: { + password: 'foo', + }, + }, + }, + error: 'The password_confirmation field and old.password field must be different', + }, + { + rule: notSameAsRule({ otherField: 'old.password' }), + value: 'foo', + field: { + name: 'password_confirmation', + parent: { + password: '', + }, + data: { + old: { + password: 'bar', + }, + }, + }, + }, + { + rule: notSameAsRule({ otherField: 'old.password' }), + value: 'foo', + field: { + name: 'password_confirmation', + parent: { + password: '', + }, + data: {}, + }, + }, ]) .run(stringRuleValidator) })