|
| 1 | +component extends="org.lucee.cfml.test.LuceeTestCase" labels="ast" { |
| 2 | + |
| 3 | + variables.testDir = getDirectoryFromPath( getCurrentTemplatePath() ) & "LDEV5989/"; |
| 4 | + |
| 5 | + function run( testResults, testBox ) { |
| 6 | + |
| 7 | + describe( "LDEV-5989: Interpolated attribute values have escaped hashes in raw field", function() { |
| 8 | + |
| 9 | + it( "raw should contain original single hashes for interpolated attributes", function() { |
| 10 | + var code = fileRead( variables.testDir & "interpolatedAttr.cfm" ); |
| 11 | + var ast = astFromString( code ); |
| 12 | + |
| 13 | + // Find the condition attribute |
| 14 | + var attr = findAttribute( ast, "condition" ); |
| 15 | + expect( attr ).notToBeNull( "condition attribute should be present in AST" ); |
| 16 | + |
| 17 | + // The raw field should contain single hashes like the original source |
| 18 | + // NOT escaped hashes like "##it.hasNext()##" |
| 19 | + var rawValue = attr.value.raw; |
| 20 | + |
| 21 | + // Check for doubled hashes (##) in raw - use chr(35) to avoid CFML escaping issues |
| 22 | + // If raw has ##, find() will locate chr(35)&chr(35) sequence |
| 23 | + var doubleHash = chr( 35 ) & chr( 35 ); |
| 24 | + var hasDoubleHash = find( doubleHash, rawValue ) > 0; |
| 25 | + |
| 26 | + // If hasDoubleHash is true, raw contains ## which is the bug |
| 27 | + expect( hasDoubleHash ).toBeFalse( "Raw should contain single hashes, not escaped ## - got: " & rawValue ); |
| 28 | + }); |
| 29 | + |
| 30 | + it( "round-trip should preserve interpolated attribute values", function() { |
| 31 | + // Parse file with interpolated attribute |
| 32 | + var code1 = fileRead( variables.testDir & "roundtrip1.cfm" ); |
| 33 | + var ast1 = astFromString( code1 ); |
| 34 | + |
| 35 | + var attr1 = findAttribute( ast1, "condition" ); |
| 36 | + expect( attr1 ).notToBeNull( "First parse should find condition attribute" ); |
| 37 | + var raw1 = attr1.value.raw; |
| 38 | + |
| 39 | + // Build round-trip file using template |
| 40 | + var template = fileRead( variables.testDir & "loopTemplate.txt" ); |
| 41 | + var code2 = replace( template, "%%CONDITION%%", raw1 ); |
| 42 | + fileWrite( variables.testDir & "roundtrip2.cfm", code2 ); |
| 43 | + |
| 44 | + // Parse the round-trip file |
| 45 | + var ast2 = astFromString( code2 ); |
| 46 | + var attr2 = findAttribute( ast2, "condition" ); |
| 47 | + expect( attr2 ).notToBeNull( "Second parse should find condition attribute" ); |
| 48 | + |
| 49 | + // Raw should be stable - not doubling hashes each round |
| 50 | + expect( attr2.value.raw ).toBe( raw1, "Raw should be stable after round-trip, not doubling hashes" ); |
| 51 | + }); |
| 52 | + |
| 53 | + }); |
| 54 | + |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * Recursively find an Attribute node by name |
| 59 | + */ |
| 60 | + private function findAttribute( required struct node, required string name ) { |
| 61 | + if ( ( node.type ?: "" ) == "Attribute" && ( node.name ?: "" ) == name ) { |
| 62 | + return node; |
| 63 | + } |
| 64 | + for ( var key in node ) { |
| 65 | + var val = node[ key ]; |
| 66 | + if ( isStruct( val ) ) { |
| 67 | + var result = findAttribute( val, name ); |
| 68 | + if ( !isNull( result ) ) return result; |
| 69 | + } else if ( isArray( val ) ) { |
| 70 | + for ( var item in val ) { |
| 71 | + if ( isStruct( item ) ) { |
| 72 | + var result = findAttribute( item, name ); |
| 73 | + if ( !isNull( result ) ) return result; |
| 74 | + } |
| 75 | + } |
| 76 | + } |
| 77 | + } |
| 78 | + return; |
| 79 | + } |
| 80 | + |
| 81 | +} |
0 commit comments