Skip to content

Commit dd0dd48

Browse files
authored
Merge pull request #175 from mrigsby/lockedDataProps
Locked data props
2 parents 459fe3a + 96618c0 commit dd0dd48

8 files changed

+377
-159
lines changed

models/Component.cfc

Lines changed: 151 additions & 134 deletions
Large diffs are not rendered by default.

test-harness/tests/specs/CBWIRESpec.cfc

Lines changed: 119 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ component extends="coldbox.system.testing.BaseTestCase" {
191191
var result = CBWIREController.wire( "test.should_support_computed_properties" );
192192
expect( reFindNoCase( "UUID: [A-Za-z0-9-]+", result ) ).toBeGT( 0 );
193193
} );
194-
194+
195195
it( "should cache computed properties", function() {
196196
var result = CBWIREController.wire( "test.should_cache_computed_properties" );
197197
var firstUUID = reFindNoCase( "UUID: ([A-Za-z0-9-]+)", result, 1, true ).match[ 2 ];
@@ -685,7 +685,7 @@ component extends="coldbox.system.testing.BaseTestCase" {
685685
expect( response.components[1].effects.html ).toInclude( "CBWIRE Slays!" );
686686
} );
687687

688-
it( "should run action we pass it with parameters", function() {
688+
it( "should run action we pass it with parameters", function() {
689689
var payload = incomingRequest(
690690
memo = {
691691
"name": "TestComponent",
@@ -704,7 +704,7 @@ component extends="coldbox.system.testing.BaseTestCase" {
704704
],
705705
updates = {}
706706
);
707-
707+
708708
var response = cbwireController.handleRequest( payload, event );
709709
expect( response.components[1].effects.html ).toInclude( "Title: Hello world from CBWIRE!" );
710710
} );
@@ -1128,6 +1128,100 @@ component extends="coldbox.system.testing.BaseTestCase" {
11281128
var response = cbwireController.handleRequest( payload, event );
11291129
expect( response.components[1].effects.html ).toInclude( "CBWIRE Slaps!" );
11301130
} );
1131+
1132+
it( "should throw a CBWIREException when trying to update a locked property (array)", function() {
1133+
var payload = incomingRequest(
1134+
memo = {
1135+
"name": "test.should_throw_exception_on_locked_data_property_array",
1136+
"id": "Z1Ruz1tGMPXSfw7osBW2",
1137+
"children": []
1138+
},
1139+
data = {},
1140+
calls = [],
1141+
updates = { "lockedPropertyKey" : "Changed Value" }
1142+
);
1143+
expect( function() {
1144+
cbwireController.handleRequest( payload, event );
1145+
} ).toThrow( message="The property lockedPropertyKey is locked and cannot be updated." );
1146+
} );
1147+
1148+
it( "should throw a CBWIREException when trying to update a locked property (list)", function() {
1149+
var payload = incomingRequest(
1150+
memo = {
1151+
"name": "test.should_throw_exception_on_locked_data_property_list",
1152+
"id": "Z1Ruz1tGMPXSfw7osBW2",
1153+
"children": []
1154+
},
1155+
data = {},
1156+
calls = [],
1157+
updates = { "lockedPropertyKey" : "Changed Value" }
1158+
);
1159+
expect( function() {
1160+
cbwireController.handleRequest( payload, event );
1161+
} ).toThrow( message="The property lockedPropertyKey is locked and cannot be updated." );
1162+
} );
1163+
1164+
it( "should throw a CBWIREException when trying to update a locked property (string)", function() {
1165+
var payload = incomingRequest(
1166+
memo = {
1167+
"name": "test.should_throw_exception_on_locked_data_property_string",
1168+
"id": "Z1Ruz1tGMPXSfw7osBW2",
1169+
"children": []
1170+
},
1171+
data = {},
1172+
calls = [],
1173+
updates = { "lockedPropertyKey" : "Changed Value" }
1174+
);
1175+
expect( function() {
1176+
cbwireController.handleRequest( payload, event );
1177+
} ).toThrow( message="The property lockedPropertyKey is locked and cannot be updated." );
1178+
} );
1179+
1180+
it( "should not throw an error when empty array is used for locked property ", function() {
1181+
var payload = incomingRequest(
1182+
memo = {
1183+
"name": "test.should_not_throw_exception_on_locked_data_property_empty_array",
1184+
"id": "Z1Ruz1tGMPXSfw7osBW2",
1185+
"children": []
1186+
},
1187+
data = {},
1188+
calls = [],
1189+
updates = { "lockedPropertyKey" : "Changed Value" }
1190+
);
1191+
var response = cbwireController.handleRequest( payload, event );
1192+
expect( response.components[1].effects.html ).toInclude( "Locked Property Value: Changed Value" );
1193+
} );
1194+
1195+
it( "should not throw an error when empty string is used for locked property ", function() {
1196+
var payload = incomingRequest(
1197+
memo = {
1198+
"name": "test.should_not_throw_exception_on_locked_data_property_empty_string",
1199+
"id": "Z1Ruz1tGMPXSfw7osBW2",
1200+
"children": []
1201+
},
1202+
data = {},
1203+
calls = [],
1204+
updates = { "lockedPropertyKey" : "Changed Value" }
1205+
);
1206+
var response = cbwireController.handleRequest( payload, event );
1207+
expect( response.components[1].effects.html ).toInclude( "Locked Property Value: Changed Value" );
1208+
} );
1209+
1210+
it( "should not throw an error when data type other than array or string is used for locked property ", function() {
1211+
var payload = incomingRequest(
1212+
memo = {
1213+
"name": "test.should_not_throw_exception_on_locked_data_property_other",
1214+
"id": "Z1Ruz1tGMPXSfw7osBW2",
1215+
"children": []
1216+
},
1217+
data = {},
1218+
calls = [],
1219+
updates = { "lockedPropertyKey" : "Changed Value" }
1220+
);
1221+
var response = cbwireController.handleRequest( payload, event );
1222+
expect( response.components[1].effects.html ).toInclude( "Locked Property Value: Changed Value" );
1223+
} );
1224+
11311225
} );
11321226

11331227
describe("File Uploads", function() {
@@ -1360,7 +1454,7 @@ component extends="coldbox.system.testing.BaseTestCase" {
13601454
it( "throws error if it's unable to find a module component", function() {
13611455
expect( function() {
13621456
var result = cbwireController.wire( "missing@someModule" );
1363-
} ).toThrow( type="ModuleNotFound" );
1457+
} ).toThrow( type="ModuleNotFound" );
13641458
} );
13651459

13661460
it( "can render component from nested module using default wires location", function() {
@@ -1383,41 +1477,41 @@ component extends="coldbox.system.testing.BaseTestCase" {
13831477
preprocessor = getInstance("CBWIREPreprocessor@cbwire");
13841478
prepareMock( preprocessor );
13851479
});
1386-
1480+
13871481
it("should parse and replace single cbwire tag with no arguments", function() {
13881482
var input = "<cbwire:testComponent/>";
13891483
var expected = "##wire( name=""testComponent"", params={ }, lazy=false )##";
13901484
var result = preprocessor.handle(input);
13911485
expect(result).toBe(expected);
13921486
});
1393-
1487+
13941488
it("should parse and replace cbwire tag with multiple arguments", function() {
13951489
var input = "<cbwire:testComponent :param1='value1' param2='value2'/>";
13961490
var expected = "##wire( name=""testComponent"", params={ param1=value1, param2='value2' }, lazy=false )##";
13971491
var result = preprocessor.handle(input);
13981492
expect(result).toBe(expected);
13991493
});
1400-
1494+
14011495
it("should correctly handle arguments with expressions", function() {
14021496
var input = "<cbwire:testComponent :expr='someExpression'/>";
14031497
var expected = "##wire( name=""testComponent"", params={ expr=someExpression }, lazy=false )##";
14041498
var result = preprocessor.handle(input);
14051499
expect(result).toBe(expected);
14061500
});
1407-
1501+
14081502
it("should maintain order and syntax of multiple attributes", function() {
14091503
var input = "<cbwire:testComponent attr1='foo' :expr='bar' attr2='baz'/>";
14101504
var expected = "##wire( name=""testComponent"", params={ attr1='foo', expr=bar, attr2='baz' }, lazy=false )##";
14111505
var result = preprocessor.handle(input);
14121506
expect(result).toBe(expected);
14131507
});
1414-
1508+
14151509
it("should replace multiple cbwire tags in a single content string", function() {
14161510
var input = "Here is a test <cbwire:firstComponent attr='value'/> and another <cbwire:secondComponent :expr='expression'/>";
14171511
var expected = "Here is a test ##wire( name=""firstComponent"", params={ attr='value' }, lazy=false )## and another ##wire( name=""secondComponent"", params={ expr=expression }, lazy=false )##";
14181512
var result = preprocessor.handle(input);
14191513
expect(result).toBe(expected);
1420-
});
1514+
});
14211515

14221516
it("should throw an exception for unparseable tags", function() {
14231517
var input = "<cbwire:testComponent :broken='noEndQuote>";
@@ -1434,56 +1528,56 @@ component extends="coldbox.system.testing.BaseTestCase" {
14341528
// Create an instance of the component that handles teleport preprocessing
14351529
preprocessor = getInstance( "TeleportPreprocessor@cbwire" );
14361530
});
1437-
1531+
14381532
it("should replace @teleport with the correct template tag", function() {
14391533
var content = "@teleport(selector) content @endteleport";
14401534
var expected = '<template x-teleport="selector"> content </template>';
14411535
var result = preprocessor.handle(content);
14421536
expect(result).toBe(expected);
14431537
});
1444-
1538+
14451539
it("should handle single quotes around the selector", function() {
14461540
var content = "@teleport('selector') content @endteleport";
14471541
var expected = '<template x-teleport="selector"> content </template>';
14481542
var result = preprocessor.handle(content);
14491543
expect(result).toBe(expected);
14501544
});
1451-
1545+
14521546
it("should handle double quotes around the selector", function() {
14531547
var content = '@teleport("selector") content @endteleport';
14541548
var expected = '<template x-teleport="selector"> content </template>';
14551549
var result = preprocessor.handle(content);
14561550
expect(result).toBe(expected);
14571551
});
1458-
1552+
14591553
it("should handle no quotes around the selector", function() {
14601554
var content = "@teleport(selector) content @endteleport";
14611555
var expected = '<template x-teleport="selector"> content </template>';
14621556
var result = preprocessor.handle(content);
14631557
expect(result).toBe(expected);
14641558
});
1465-
1559+
14661560
it("should handle spaces within the parentheses", function() {
14671561
var content = "@teleport( selector ) content @endteleport";
14681562
var expected = '<template x-teleport="selector"> content </template>';
14691563
var result = preprocessor.handle(content);
14701564
expect(result).toBe(expected);
14711565
});
1472-
1566+
14731567
it("should handle multiple teleport directives in the same content", function() {
14741568
var content = "@teleport(selector1) content1 @endteleport @teleport(selector2) content2 @endteleport";
14751569
var expected = '<template x-teleport="selector1"> content1 </template> <template x-teleport="selector2"> content2 </template>';
14761570
var result = preprocessor.handle(content);
14771571
expect(result).toBe(expected);
14781572
});
1479-
1573+
14801574
it("should handle nested teleport directives", function() {
14811575
var content = "@teleport(outer) @teleport(inner) content @endteleport @endteleport";
14821576
var expected = '<template x-teleport="outer"> <template x-teleport="inner"> content </template> </template>';
14831577
var result = preprocessor.handle(content);
14841578
expect(result).toBe(expected);
14851579
});
1486-
1580+
14871581
it("should not alter content without teleport directives", function() {
14881582
var content = "Normal content without directives";
14891583
var result = preprocessor.handle(content);
@@ -1497,9 +1591,9 @@ component extends="coldbox.system.testing.BaseTestCase" {
14971591
/**
14981592
* Helper test method for creating incoming request payloads
14991593
*
1500-
* @data
1594+
* @data
15011595
* @calls
1502-
*
1596+
*
15031597
* @return struct
15041598
*/
15051599
private function incomingRequest(
@@ -1539,24 +1633,24 @@ component extends="coldbox.system.testing.BaseTestCase" {
15391633
/**
15401634
* Take a rendered HTML component and breaks out its snapshot,
15411635
* effects, etc for analysis during tests.
1542-
*
1636+
*
15431637
* @return struct
15441638
*/
15451639
private function parseRendering( html, index = 1 ) {
15461640
local.result = {};
15471641
// Determine outer element
15481642
local.outerElementMatches = reMatchNoCase( "<([a-z]+)\s*", html );
15491643
local.result[ "outerElement" ] = reFindNoCase( "<([a-z]+)\s*", html, 1, true ).match[ 2 ];
1550-
// Parse snapshot
1644+
// Parse snapshot
15511645
local.result[ "snapshot" ] = parseSnapshot( html, index );
1552-
// Parse effects
1646+
// Parse effects
15531647
local.result[ "effects" ] = parseEffects( html, index );
15541648
return local.result;
15551649
}
15561650

15571651
/**
15581652
* Parse the snapshot from a rendered HTML component
1559-
*
1653+
*
15601654
* @return struct
15611655
*/
15621656
private function parseSnapshot( html, index = 1 ) {
@@ -1569,7 +1663,7 @@ component extends="coldbox.system.testing.BaseTestCase" {
15691663

15701664
/**
15711665
* Parse the effects from a rendered HTML component
1572-
*
1666+
*
15731667
* @return struct
15741668
*/
15751669
private function parseEffects( html, index = 1 ) {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<cfoutput>
2+
<div>
3+
<h1>Should Not Throw Exception</h1>
4+
<p>When a locked property is an empty array the wire should ignore continue.</p>
5+
<p>Locked Property Value: #lockedPropertyKey#</p>
6+
</div>
7+
</cfoutput>
8+
9+
<cfscript>
10+
// @startWire
11+
data = {
12+
"lockedPropertyKey": "I AM NOT LOCKED!"
13+
};
14+
15+
locked = [];
16+
17+
// @endWire
18+
</cfscript>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<cfoutput>
2+
<div>
3+
<h1>Should Not Throw Exception</h1>
4+
<p>When a locked property is an empty string the wire should ignore continue.</p>
5+
<p>Locked Property Value: #lockedPropertyKey#</p>
6+
</div>
7+
</cfoutput>
8+
9+
<cfscript>
10+
// @startWire
11+
data = {
12+
"lockedPropertyKey": "I AM NOT LOCKED!"
13+
};
14+
15+
locked = "";
16+
17+
// @endWire
18+
</cfscript>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<cfoutput>
2+
<div>
3+
<h1>Should Not Throw Exception</h1>
4+
<p>When a locked property is a data type other than an array, string/list the wire should ignore continue.</p>
5+
<p>Locked Property Value: #lockedPropertyKey#</p>
6+
</div>
7+
</cfoutput>
8+
9+
<cfscript>
10+
// @startWire
11+
data = {
12+
"lockedPropertyKey": "I AM NOT LOCKED!"
13+
};
14+
15+
locked = { "lockedPropertyKey" : "someValue" };
16+
17+
// @endWire
18+
</cfscript>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<cfoutput>
2+
<div>
3+
<h1>Should Throw Exception on Locked Property</h1>
4+
<p>When a property is locked with an array, it should throw an exception when trying to set any of the keys in the array.</p>
5+
</div>
6+
</cfoutput>
7+
8+
<cfscript>
9+
// @startWire
10+
data = {
11+
"lockedPropertyKey": "I AM LOCKED!"
12+
};
13+
14+
locked = ["lockedPropertyKey"];
15+
16+
// @endWire
17+
</cfscript>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<cfoutput>
2+
<div>
3+
<h1>Should Throw Exception on Locked Property</h1>
4+
<p>When a property is locked with an list, it should throw an exception when trying to set any of the keys in the list.</p>
5+
</div>
6+
</cfoutput>
7+
8+
<cfscript>
9+
// @startWire
10+
data = {
11+
"lockedPropertyKey": "I AM LOCKED!",
12+
"lockedPropertyKeyTwo": "I AM ALSO LOCKED!",
13+
"lockedPropertyKeyThree": "I AM LOCKED AS WELL!"
14+
};
15+
16+
locked = "lockedPropertyKeyThree,lockedPropertyKey";
17+
18+
// @endWire
19+
</cfscript>

0 commit comments

Comments
 (0)