diff --git a/models/Component.cfc b/models/Component.cfc index 606bf24..9912825 100644 --- a/models/Component.cfc +++ b/models/Component.cfc @@ -360,7 +360,7 @@ component output="true" accessors="true" { // Determine if component should be lazy loaded // If lazy parameter is explicitly provided, use that value // Otherwise, use the component's lazy preference - local.shouldLazyLoad = isNull( arguments.lazy ) ? + local.shouldLazyLoad = isNull( arguments.lazy ) ? local.instance._getLazyLoad() : // Use component's preference if no explicit parameter arguments.lazy; // Use explicit parameter value @@ -1115,7 +1115,9 @@ component output="true" accessors="true" { function _getConstraints(){ if ( variables.keyExists( "constraints" ) ) { return variables.constraints; - } + }else if ( structKeyExists( this, "constraints" ) ) { + return this.constraints; + } return [:]; } diff --git a/models/services/ValidationService.cfc b/models/services/ValidationService.cfc index 81b2dc2..0894415 100644 --- a/models/services/ValidationService.cfc +++ b/models/services/ValidationService.cfc @@ -45,8 +45,12 @@ component accessors="true" singleton { * @return ValidationResult The result of the validation operation. */ function validate( wire, target, fields, constraints, locale, excludeFields, includeFields, profiles ){ - arguments.target = isNull( arguments.target ) ? arguments.wire._getDataProperties() : arguments.target; - arguments.constraints = isNull( arguments.constraints ) ? arguments.wire._getConstraints() : arguments.constraints; + if( isNull( arguments.target ) ){ + // if no target is provided, default to the wire's data properties + arguments.target = arguments.wire._getDataProperties(); + // use the wire's constraints if explicit constraints are not provided + arguments.constraints = isNull( arguments.constraints ) ? arguments.wire._getConstraints() : arguments.constraints + } return getManager().validate( argumentCollection = arguments ); } diff --git a/test-harness/models/validationTest.cfc b/test-harness/models/validationTest.cfc new file mode 100644 index 0000000..d172f17 --- /dev/null +++ b/test-harness/models/validationTest.cfc @@ -0,0 +1,21 @@ +component accessors="true" { + + property name="FirstName"; + property name="LastName"; + + this.constraints = { + FirstName : { + required : true, + requiredMessage : "First name is required", + size : "2..50", + sizeMessage : "First name must be 2-50 characters" + }, + LastName : { + required : true, + requiredMessage : "Last name is required", + size : "2..50", + sizeMessage : "Last name must be 2-50 characters" + } + }; + +} \ No newline at end of file diff --git a/test-harness/tests/specs/CBWIRESpec.cfc b/test-harness/tests/specs/CBWIRESpec.cfc index 5c5aafe..a90b7ea 100644 --- a/test-harness/tests/specs/CBWIRESpec.cfc +++ b/test-harness/tests/specs/CBWIRESpec.cfc @@ -575,9 +575,26 @@ component extends="coldbox.system.testing.BaseTestCase" { ); expect( renderedHtml ).toInclude( 'Some&##x20;text&##x20;with&##x20;&##x5c;"quotes' ); - }); + } ); + + it( "should get constraints when set using this.constraints=", function() { + var testValidationComponent = getInstance("wires.test.validation.validateConstraints1"); + var constraints = testValidationComponent._getConstraints(); + expect( constraints ).toBeTypeOf( "struct" ); + expect( constraints ).toHaveLength( 1 ); + expect( constraints ).toHaveKey( "firstname" ); + } ); + + it( "should get constraints when set using constraints=", function() { + var testValidationComponent = getInstance("wires.test.validation.validateConstraints2"); + var constraints = testValidationComponent._getConstraints(); + expect( constraints ).toBeTypeOf( "struct" ); + expect( constraints ).toHaveLength( 1 ); + expect( constraints ).toHaveKey( "firstname" ); + } ); - }); + + } ); describe("Incoming Requests", function() { @@ -727,7 +744,7 @@ component extends="coldbox.system.testing.BaseTestCase" { var settings = getInstance( "coldbox:modulesettings:cbwire" ); var originalSetting = settings.csrfEnabled; settings.csrfEnabled = false; - + var payload = incomingRequest( memo = { "name": "TestComponent", @@ -747,12 +764,12 @@ component extends="coldbox.system.testing.BaseTestCase" { updates = {}, csrfToken = "badToken" ); - + // Should not throw an error even with bad token when CSRF is disabled var response = cbwireController.handleRequest( payload, event ); expect( isStruct( response ) ).toBeTrue(); expect( response.components[1].effects.html ).toInclude( "CBWIRE Slays!" ); - + // Restore original setting settings.csrfEnabled = originalSetting; } ); @@ -761,10 +778,10 @@ component extends="coldbox.system.testing.BaseTestCase" { var settings = getInstance( "coldbox:modulesettings:cbwire" ); var originalSetting = settings.csrfEnabled; settings.csrfEnabled = false; - + var token = cbwireController.generateCSRFToken(); expect( token ).toBe( "" ); - + // Restore original setting settings.csrfEnabled = originalSetting; } ); diff --git a/test-harness/tests/specs/unit/services/ValidationServiceSpec.cfc b/test-harness/tests/specs/unit/services/ValidationServiceSpec.cfc index 559acf2..83f9b5b 100644 --- a/test-harness/tests/specs/unit/services/ValidationServiceSpec.cfc +++ b/test-harness/tests/specs/unit/services/ValidationServiceSpec.cfc @@ -40,7 +40,8 @@ component extends="coldbox.system.testing.BaseTestCase" { }); describe( "validate()", function() { - it( "should validate with given target and constraints", function() { + + it( "should validate with given target and constraints", function() { var wire = {}; var target = { "foo": "bar" }; var constraints = { "foo": { "required": true } }; @@ -61,6 +62,83 @@ component extends="coldbox.system.testing.BaseTestCase" { var result = validationService.validate( wire ); expect( result ).toHaveKey( "ok" ); }); + + it( "should validate an object other than the wire itself", function() { + var oValidationTest = getInstance( "validationTest" ); + var wire = prepareMock( createStub() ); + wire.$( "_getDataProperties", { "bar": "baz" } ); + wire.$( "_getConstraints", { "bar": { "required": true } } ); + // mock the validate method to return the arguments passed to it so that + // we can validate that the correct mutation of the arguments is occurring + // in the validate() method of the service + mockValidationManager.$( + method = "validate", + callback = function() { + return arguments; + } + ); + var result = validationService.validate( wire=wire, target=oValidationTest ); + // validate that we do not have the wires constraints and that there + // are no constraints passed to validate() method so that the constraints + // from the object will be used + expect( result ).notToHaveKey( "constraints" ); + // validate that we have a target passed into the validate method + expect( result ).toHaveKey( "target" ); + // validate that the target contains the constraints from the object + expect( result.target ).toHaveKey( "constraints" ); + expect( result.target.constraints ).toBeTypeOf( "struct" ); + expect( result.target.constraints ).toHaveLength( 2 ); + expect( result.target.constraints ).toHaveKey( "firstname" ); + expect( result.target.constraints ).toHaveKey( "lastname" ); + expect( result.target.constraints ).notToHaveKey( "bar" ); + // get the metadata of the target to validate that it is the correct object and not the wire + var resultTargetMetaData = getMetadata( result.target ); + // validate that the target metadata contains the name of the component (validationTest) + expect( resultTargetMetaData ).toHaveKey( "name" ); + expect( resultTargetMetaData.name ).toInclude( "validationTest" ); + }); + + it( "shoud validate an object other than the wire itself with custom validation constraints", function() { + var oValidationTest = getInstance( "validationTest" ); + var wire = prepareMock( createStub() ); + wire.$( "_getDataProperties", { "bar": "baz" } ); + wire.$( "_getConstraints", { "bar": { "required": true } } ); + var customConstraints = { + password : { + required : true, + requiredMessage : "Password is required", + size : "8..50", + sizeMessage : "Password must be 8-50 characters" + } + }; + // mock the validate method to return the arguments passed to it so that + // we can validate that the correct mutation of the arguments is occurring + // in the validate() method of the service + mockValidationManager.$( + method = "validate", + callback = function() { + return arguments; + } + ); + var result = validationService.validate( wire=wire, target=oValidationTest, constraints=customConstraints ); + // validate that we do not have the wires constraints and that the constraints + // passed to the validate() method are the custom constraints and not the objects + // or wires constraints + expect( result ).toHaveKey( "constraints" ); + expect( result.constraints ).toBeTypeOf( "struct" ); + expect( result.constraints ).toHaveLength( 1 ); + expect( result.constraints ).toHaveKey( "password" ); + expect( result.constraints ).notToHaveKey( "lastname" ); + expect( result.constraints ).notToHaveKey( "bar" ); + // validate that we have a target passed into the validate method + expect( result ).toHaveKey( "target" ); + // get the metadata of the target to validate that it is the correct object and not the wire + var resultTargetMetaData = getMetadata( result.target ); + // validate that the target metadata contains the name of the component (validationTest) + expect( resultTargetMetaData ).toHaveKey( "name" ); + expect( resultTargetMetaData.name ).toInclude( "validationTest" ); + }); + }); describe( "validateOrFail()", function() { diff --git a/test-harness/wires/test/validation/validateConstraints1.cfc b/test-harness/wires/test/validation/validateConstraints1.cfc new file mode 100644 index 0000000..70d89dd --- /dev/null +++ b/test-harness/wires/test/validation/validateConstraints1.cfc @@ -0,0 +1,20 @@ +component extends="cbwire.models.Component" { + + data = { + "firstname": "sdfsad", + "lastname": "" + }; + /* + This components DOES use this.constraints= + + The cbwire documentation states that you can define constraints using constraints= + https://cbwire.ortusbooks.com/features/form-validation + + CBValidation documentation states that you define constraints using this.constraints= + https://coldbox-validation.ortusbooks.com/overview/declaring-constraints/domain-object + */ + this.constraints = { + "firstname": { required: true } + }; + +} \ No newline at end of file diff --git a/test-harness/wires/test/validation/validateConstraints1.cfm b/test-harness/wires/test/validation/validateConstraints1.cfm new file mode 100644 index 0000000..3534b82 --- /dev/null +++ b/test-harness/wires/test/validation/validateConstraints1.cfm @@ -0,0 +1,6 @@ + +
+ + +
+
\ No newline at end of file diff --git a/test-harness/wires/test/validation/validateConstraints2.cfc b/test-harness/wires/test/validation/validateConstraints2.cfc new file mode 100644 index 0000000..2955ebe --- /dev/null +++ b/test-harness/wires/test/validation/validateConstraints2.cfc @@ -0,0 +1,20 @@ +component extends="cbwire.models.Component" { + + data = { + "firstname": "sdfsad", + "lastname": "" + }; + /* + This components DOES use constraints= + + The cbwire documentation states that you can define constraints using constraints= + https://cbwire.ortusbooks.com/features/form-validation + + CBValidation documentation states that you define constraints using this.constraints= + https://coldbox-validation.ortusbooks.com/overview/declaring-constraints/domain-object + */ + constraints = { + "firstname": { required: true } + }; + +} \ No newline at end of file diff --git a/test-harness/wires/test/validation/validateConstraints2.cfm b/test-harness/wires/test/validation/validateConstraints2.cfm new file mode 100644 index 0000000..3534b82 --- /dev/null +++ b/test-harness/wires/test/validation/validateConstraints2.cfm @@ -0,0 +1,6 @@ + +
+ + +
+
\ No newline at end of file