1+ import { TextDocument , Position } from 'vscode-languageserver' ;
2+ import { getAuraBindingValue , parse } from '../aura-utils' ;
3+
4+ describe ( 'getAuraBindingValue' , ( ) => {
5+ function createDocument ( content : string ) : TextDocument {
6+ return TextDocument . create ( 'file:///test.cmp' , 'html' , 0 , content ) ;
7+ }
8+
9+ function createPosition ( line : number , character : number ) : Position {
10+ return Position . create ( line , character ) ;
11+ }
12+
13+ function getBindingValue ( content : string , line : number , character : number ) : string | null {
14+ const document = createDocument ( content ) ;
15+ const htmlDocument = parse ( content ) ;
16+ const position = createPosition ( line , character ) ;
17+ console . log ( 'position' , position ) ;
18+ return getAuraBindingValue ( document , position , htmlDocument ) ;
19+ }
20+
21+ // simple function to find the cursor position in a string, helpful for readability
22+ function findCursorPosition ( content : string ) : number {
23+ const cursorPosition = content . indexOf ( '|' ) ;
24+ return cursorPosition ;
25+ }
26+
27+ describe ( 'basic functionality' , ( ) => {
28+ it ( 'should extract property from v binding in attribute' , ( ) => {
29+ const content = '<div value="{!v.property}"></div>' ;
30+ const stringWithCursor = '<div value="{!v.property|}"></div>' ;
31+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
32+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.property|}"></div>
33+ expect ( result ) . toBe ( 'property' ) ;
34+ } ) ;
35+
36+ it ( 'should extract property from c binding in attribute' , ( ) => {
37+ const content = '<div onclick="{!c.handleClick}"></div>' ;
38+ const stringWithCursor = '<div onclick="{!c.handleClick|}"></div>' ;
39+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
40+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div onclick="{!c.handleClick|}"></div>
41+ expect ( result ) . toBe ( 'handleClick' ) ;
42+ } ) ;
43+
44+ it ( 'should extract property from m binding in attribute' , ( ) => {
45+ const content = '<div data="{!m.dataValue}"></div>' ;
46+ const stringWithCursor = '<div data="{!m.dataValue|}"></div>' ;
47+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
48+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div data="{!m.dataValue|}"></div>
49+ expect ( result ) . toBe ( 'dataValue' ) ;
50+ } ) ;
51+
52+ it ( 'should return null when cursor is before dot' , ( ) => {
53+ const content = '<div value="{!v.property}"></div>' ;
54+ const stringWithCursor = '<div value="{!v|.property}"></div>' ;
55+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
56+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v|.property}"></div>
57+ expect ( result ) . toBe ( 'property' ) ;
58+ } ) ;
59+
60+ it ( 'should handle quoted attribute values' , ( ) => {
61+ const content = '<div value="\'{!v.property}\'"></div>' ;
62+ const stringWithCursor = '<div value="\'{!v.property|}\'"></div>' ;
63+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
64+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value='{!v.property|}'></div>
65+ expect ( result ) . toBe ( 'property' ) ;
66+ } ) ;
67+
68+ it ( 'should handle double quoted attribute values' , ( ) => {
69+ const content = '<div value=\'"{!v.property}"\'></div>' ;
70+ const stringWithCursor = '<div value=\'"{!v.property|}"\'></div>' ;
71+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
72+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value='"{!v.property|}"'></div>
73+ expect ( result ) . toBe ( 'property' ) ;
74+ } ) ;
75+
76+ it ( 'should return null for non-binding attributes' , ( ) => {
77+ const content = '<div class="some-class"></div>' ;
78+ const stringWithCursor = '<div class="some-|class"></div>' ;
79+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
80+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div class="some-|class"></div>
81+ expect ( result ) . toBeNull ( ) ;
82+ } ) ;
83+ } ) ;
84+
85+ describe ( 'body text bindings' , ( ) => {
86+ it ( 'should extract property from v binding in body text' , ( ) => {
87+ const content = '<div>{!v.property}</div>' ;
88+ const stringWithCursor = '<div>{!v.property|}</div>' ;
89+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
90+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div>{!v.property|}</div>
91+ expect ( result ) . toBe ( 'property' ) ;
92+ } ) ;
93+
94+ it ( 'should extract property from c binding in body text' , ( ) => {
95+ const content = '<div>{!c.method}</div>' ;
96+ const stringWithCursor = '<div>{!c.method|}</div>' ;
97+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
98+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div>{!c.method|}</div>
99+ expect ( result ) . toBe ( 'method' ) ;
100+ } ) ;
101+
102+ it ( 'should extract property from m binding in body text' , ( ) => {
103+ const content = '<div>{!m.data}</div>' ;
104+ const stringWithCursor = '<div>{!m.data|}</div>' ;
105+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
106+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div>{!m.data|}</div>
107+ expect ( result ) . toBe ( 'data' ) ;
108+ } ) ;
109+
110+ it ( 'should return null when cursor is before dot in body text' , ( ) => {
111+ const content = '<div>{!v.property}</div>' ;
112+ const stringWithCursor = '<div>{!v|.property|}</div>' ;
113+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
114+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div>{!v|.property|}</div>
115+ expect ( result ) . toBeNull ( ) ;
116+ } ) ;
117+
118+ it ( 'should handle multiple bindings in body text' , ( ) => {
119+ const content = '<div>{!v.first} and {!v.second}</div>' ;
120+ const stringWithCursor = '<div>{!v.first| and {!v.second}</div>' ;
121+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
122+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div>{!v.first| and {!v.second}</div>
123+ expect ( result ) . toBe ( 'first' ) ;
124+ } ) ;
125+ } ) ;
126+
127+ describe ( 'edge cases' , ( ) => {
128+ it ( 'should return null for empty document' , ( ) => {
129+ const result = getBindingValue ( '' , 0 , 0 ) ;
130+ expect ( result ) . toBeNull ( ) ;
131+ } ) ;
132+
133+ it ( 'should return null for invalid HTML' , ( ) => {
134+ const result = getBindingValue ( '<div>' , 0 , 0 ) ;
135+ expect ( result ) . toBeNull ( ) ;
136+ } ) ;
137+
138+ it ( 'should return null when cursor is outside any node' , ( ) => {
139+ const content = '<div>{!v.property}</div>' ;
140+ const result = getBindingValue ( content , 1 , 0 ) ; // Position on new line
141+ expect ( result ) . toBeNull ( ) ;
142+ } ) ;
143+
144+ it ( 'should handle expressions with special characters in property names' , ( ) => {
145+ const content = '<div value="{!v.property_name}"></div>' ;
146+ const stringWithCursor = '<div value="{!v.property_name|}"></div>' ;
147+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
148+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.property_name|}"></div>
149+ expect ( result ) . toBe ( 'property_name' ) ;
150+ } ) ;
151+
152+ it ( 'should handle expressions with numbers in property names' , ( ) => {
153+ const content = '<div value="{!v.property123}"></div>' ;
154+ const stringWithCursor = '<div value="{!v.property123|}"></div>' ;
155+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
156+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.property123|}"></div>
157+ expect ( result ) . toBe ( 'property123' ) ;
158+ } ) ;
159+
160+ it ( 'should return "" for incomplete expressions' , ( ) => {
161+ const content = '<div value="{!v.}"></div>' ;
162+ const stringWithCursor = '<div value="{!v.|}"></div>' ;
163+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
164+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.|}"></div>
165+ expect ( result ) . toBe ( "" ) ;
166+ } ) ;
167+
168+ it ( 'should extract property when cursor is within property name in attribute' , ( ) => {
169+ const content = '<div value="{!v.property}"></div>' ;
170+ const stringWithCursor = '<div value="{!v.prop|erty|}"></div>' ;
171+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
172+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.prop|erty|}"></div>
173+ expect ( result ) . toBe ( 'property' ) ;
174+ } ) ;
175+
176+ it ( 'should extract property when cursor is within property name in body text' , ( ) => {
177+ const content = '<div>{!v.property}</div>' ;
178+ const stringWithCursor = '<div>{!v.prop|erty}</div>' ;
179+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
180+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div>{!v.prop|erty}</div>
181+ expect ( result ) . toBe ( 'property' ) ;
182+ } ) ;
183+
184+ it ( 'should extract property when cursor is at start of property name' , ( ) => {
185+ const content = '<div value="{!v.property}"></div>' ;
186+ const stringWithCursor = '<div value="{!v.|property}"></div>' ;
187+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
188+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.|property}"></div>
189+ expect ( result ) . toBe ( 'property' ) ;
190+ } ) ;
191+
192+ it ( 'should extract property when cursor is at end of property name' , ( ) => {
193+ const content = '<div value="{!v.property}"></div>' ;
194+ const stringWithCursor = '<div value="{!v.propert|y}"></div>' ;
195+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
196+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.propert|y}"></div>
197+ expect ( result ) . toBe ( 'property' ) ;
198+ } ) ;
199+ } ) ;
200+
201+ describe ( 'complex scenarios' , ( ) => {
202+ it ( 'should handle multiple attributes with bindings' , ( ) => {
203+ const content = '<div value="{!v.property}" onclick="{!c.method}"></div>' ;
204+ const stringWithCursor = '<div value="{!v.property|}" onclick="{!c.method}"></div>' ;
205+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
206+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.property|}" onclick="{!c.method}"></div>
207+ expect ( result ) . toBe ( 'property' ) ;
208+ } ) ;
209+
210+ it ( 'should handle self-closing tags with bindings' , ( ) => {
211+ const content = '<input value="{!v.property}" />' ;
212+ const stringWithCursor = '<input value="{!v.property|}" />' ;
213+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
214+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <input value="{!v.property|}"></input>
215+ expect ( result ) . toBe ( 'property' ) ;
216+ } ) ;
217+
218+ it ( 'should handle expressions with comments' , ( ) => {
219+ const content = '<div value="{!v.property}<!-- comment -->"></div>' ;
220+ const stringWithCursor = '<div value="{!v.property|}"><!-- comment --></div>' ;
221+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
222+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.property|}"><!-- comment --></div>
223+ expect ( result ) . toBe ( 'property' ) ;
224+ } ) ;
225+ } ) ;
226+
227+ describe ( 'advanced scenarios' , ( ) => {
228+ it ( 'should handle nested properties' , ( ) => {
229+ const content = '<div value="{!v.object.property}"></div>' ;
230+ const stringWithCursor = '<div value="{!v.object.property|}"></div>' ;
231+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
232+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.object.property|}"></div>
233+ expect ( result ) . toBe ( 'object' ) ; // Function returns first property in regex group
234+ } ) ;
235+
236+ it ( 'should handle expressions with multiple dots' , ( ) => {
237+ const content = '<div value="{!v.object.subObject.property}"></div>' ;
238+ const stringWithCursor = '<div value="{!v.object.subObject.property|}"></div>' ;
239+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
240+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div value="{!v.object.subObject.property|}"></div>
241+ expect ( result ) . toBe ( 'object' ) ; // Function returns first property in regex group
242+ } ) ;
243+
244+ it ( 'should handle negated expressions' , ( ) => {
245+ const content = '<div hidden="{!!v.isHidden}"></div>' ;
246+ const stringWithCursor = '<div hidden="{!!v.isHidden|}"></div>' ;
247+ const cursorPosition = findCursorPosition ( stringWithCursor ) ;
248+ const result = getBindingValue ( content , 0 , cursorPosition ) ; // Position: <div hidden="{!!v.isHidden|}"></div>
249+ expect ( result ) . toBe ( 'isHidden' ) ;
250+ } ) ;
251+ } ) ;
252+ } ) ;
0 commit comments