1
+ describe ( 'typeahead tests' , function ( ) {
2
+
3
+ beforeEach ( module ( 'ui.bootstrap.typeahead' ) ) ;
4
+ beforeEach ( module ( 'template/typeahead/typeahead.html' ) ) ;
5
+
6
+ describe ( 'syntax parser' , function ( ) {
7
+
8
+ var typeaheadParser , scope , filterFilter ;
9
+ beforeEach ( inject ( function ( _$rootScope_ , _filterFilter_ , _typeaheadParser_ ) {
10
+ typeaheadParser = _typeaheadParser_ ;
11
+ scope = _$rootScope_ ;
12
+ filterFilter = _filterFilter_ ;
13
+ } ) ) ;
14
+
15
+ it ( 'should parse the simplest array-based syntax' , function ( ) {
16
+ scope . states = [ 'Alabama' , 'California' , 'Delaware' ] ;
17
+ var result = typeaheadParser . parse ( 'state for state in states | filter:$viewValue' ) ;
18
+
19
+ var itemName = result . itemName ;
20
+ var locals = { $viewValue :'al' } ;
21
+ expect ( result . source ( scope , locals ) ) . toEqual ( [ 'Alabama' , 'California' ] ) ;
22
+
23
+ locals [ itemName ] = 'Alabama' ;
24
+ expect ( result . viewMapper ( scope , locals ) ) . toEqual ( 'Alabama' ) ;
25
+ expect ( result . modelMapper ( scope , locals ) ) . toEqual ( 'Alabama' ) ;
26
+ } ) ;
27
+
28
+ it ( 'should parse the simplest function-based syntax' , function ( ) {
29
+ scope . getStates = function ( $viewValue ) {
30
+ return filterFilter ( [ 'Alabama' , 'California' , 'Delaware' ] , $viewValue ) ;
31
+ } ;
32
+ var result = typeaheadParser . parse ( 'state for state in getStates($viewValue)' ) ;
33
+
34
+ var itemName = result . itemName ;
35
+ var locals = { $viewValue :'al' } ;
36
+ expect ( result . source ( scope , locals ) ) . toEqual ( [ 'Alabama' , 'California' ] ) ;
37
+
38
+ locals [ itemName ] = 'Alabama' ;
39
+ expect ( result . viewMapper ( scope , locals ) ) . toEqual ( 'Alabama' ) ;
40
+ expect ( result . modelMapper ( scope , locals ) ) . toEqual ( 'Alabama' ) ;
41
+ } ) ;
42
+
43
+ it ( 'should allow to specify custom model mapping that is used as a label as well' , function ( ) {
44
+
45
+ scope . states = [
46
+ { code :'AL' , name :'Alabama' } ,
47
+ { code :'CA' , name :'California' } ,
48
+ { code :'DE' , name :'Delaware' }
49
+ ] ;
50
+ var result = typeaheadParser . parse ( "state.name for state in states | filter:$viewValue | orderBy:'name':true" ) ;
51
+
52
+ var itemName = result . itemName ;
53
+ expect ( itemName ) . toEqual ( 'state' ) ;
54
+ expect ( result . source ( scope , { $viewValue :'al' } ) ) . toEqual ( [
55
+ { code :'CA' , name :'California' } ,
56
+ { code :'AL' , name :'Alabama' }
57
+ ] ) ;
58
+
59
+ var locals = { $viewValue :'al' } ;
60
+ locals [ itemName ] = { code :'AL' , name :'Alabama' } ;
61
+ expect ( result . viewMapper ( scope , locals ) ) . toEqual ( 'Alabama' ) ;
62
+ expect ( result . modelMapper ( scope , locals ) ) . toEqual ( 'Alabama' ) ;
63
+ } ) ;
64
+
65
+ it ( 'should allow to specify custom view and model mappers' , function ( ) {
66
+
67
+ scope . states = [
68
+ { code :'AL' , name :'Alabama' } ,
69
+ { code :'CA' , name :'California' } ,
70
+ { code :'DE' , name :'Delaware' }
71
+ ] ;
72
+ var result = typeaheadParser . parse ( "state.code as state.name + ' ('+state.code+')' for state in states | filter:$viewValue | orderBy:'name':true" ) ;
73
+
74
+ var itemName = result . itemName ;
75
+ expect ( result . source ( scope , { $viewValue :'al' } ) ) . toEqual ( [
76
+ { code :'CA' , name :'California' } ,
77
+ { code :'AL' , name :'Alabama' }
78
+ ] ) ;
79
+
80
+ var locals = { $viewValue :'al' } ;
81
+ locals [ itemName ] = { code :'AL' , name :'Alabama' } ;
82
+ expect ( result . viewMapper ( scope , locals ) ) . toEqual ( 'Alabama (AL)' ) ;
83
+ expect ( result . modelMapper ( scope , locals ) ) . toEqual ( 'AL' ) ;
84
+ } ) ;
85
+ } ) ;
86
+
87
+ describe ( 'typeaheadPopup - result rendering' , function ( ) {
88
+
89
+ var scope , $rootScope , $compile ;
90
+ beforeEach ( inject ( function ( _$rootScope_ , _$compile_ ) {
91
+ $rootScope = _$rootScope_ ;
92
+ scope = $rootScope . $new ( ) ;
93
+ $compile = _$compile_ ;
94
+ } ) ) ;
95
+
96
+ it ( 'should render initial results' , function ( ) {
97
+
98
+ scope . matches = [ 'foo' , 'bar' , 'baz' ] ;
99
+ scope . active = 1 ;
100
+
101
+ var el = $compile ( "<div><typeahead-popup matches='matches' active='active' select='select(activeIdx)'></typeahead-popup></div>" ) ( scope ) ;
102
+ $rootScope . $digest ( ) ;
103
+
104
+ var liElems = el . find ( 'li' ) ;
105
+ expect ( liElems . length ) . toEqual ( 3 ) ;
106
+ expect ( liElems . eq ( 0 ) ) . not . toHaveClass ( 'active' ) ;
107
+ expect ( liElems . eq ( 1 ) ) . toHaveClass ( 'active' ) ;
108
+ expect ( liElems . eq ( 2 ) ) . not . toHaveClass ( 'active' ) ;
109
+ } ) ;
110
+
111
+ it ( 'should change active item on mouseenter' , function ( ) {
112
+
113
+ scope . matches = [ 'foo' , 'bar' , 'baz' ] ;
114
+ scope . active = 1 ;
115
+
116
+ var el = $compile ( "<div><typeahead-popup matches='matches' active='active' select='select(activeIdx)'></typeahead-popup></div>" ) ( scope ) ;
117
+ $rootScope . $digest ( ) ;
118
+
119
+ var liElems = el . find ( 'li' ) ;
120
+ expect ( liElems . eq ( 1 ) ) . toHaveClass ( 'active' ) ;
121
+ expect ( liElems . eq ( 2 ) ) . not . toHaveClass ( 'active' ) ;
122
+
123
+ liElems . eq ( 2 ) . trigger ( 'mouseenter' ) ;
124
+
125
+ expect ( liElems . eq ( 1 ) ) . not . toHaveClass ( 'active' ) ;
126
+ expect ( liElems . eq ( 2 ) ) . toHaveClass ( 'active' ) ;
127
+ } ) ;
128
+
129
+ it ( 'should select an item on mouse click' , function ( ) {
130
+
131
+ scope . matches = [ 'foo' , 'bar' , 'baz' ] ;
132
+ scope . active = 1 ;
133
+ $rootScope . select = angular . noop ;
134
+ spyOn ( $rootScope , 'select' ) ;
135
+
136
+ var el = $compile ( "<div><typeahead-popup matches='matches' active='active' select='select(activeIdx)'></typeahead-popup></div>" ) ( scope ) ;
137
+ $rootScope . $digest ( ) ;
138
+
139
+ var liElems = el . find ( 'li' ) ;
140
+ liElems . eq ( 2 ) . find ( 'a' ) . trigger ( 'click' ) ;
141
+ expect ( $rootScope . select ) . toHaveBeenCalledWith ( 2 ) ;
142
+ } ) ;
143
+ } ) ;
144
+
145
+ describe ( 'typeahead' , function ( ) {
146
+
147
+ var $scope , $compile ;
148
+ var changeInputValueTo ;
149
+
150
+ beforeEach ( inject ( function ( _$rootScope_ , _$compile_ , $sniffer ) {
151
+ $scope = _$rootScope_ ;
152
+ $scope . source = [ 'foo' , 'bar' , 'baz' ] ;
153
+ $compile = _$compile_ ;
154
+
155
+ changeInputValueTo = function ( element , value ) {
156
+ var inputEl = findInput ( element ) ;
157
+ inputEl . val ( value ) ;
158
+ inputEl . trigger ( $sniffer . hasEvent ( 'input' ) ? 'input' : 'change' ) ;
159
+ $scope . $digest ( ) ;
160
+ } ;
161
+ } ) ) ;
162
+
163
+ //utility functions
164
+ var prepareInputEl = function ( inputTpl ) {
165
+ var el = $compile ( angular . element ( inputTpl ) ) ( $scope ) ;
166
+ $scope . $digest ( ) ;
167
+ return el ;
168
+ } ;
169
+
170
+ var findInput = function ( element ) {
171
+ return element . find ( 'input' ) ;
172
+ } ;
173
+
174
+ var findDropDown = function ( element ) {
175
+ return element . find ( 'div.dropdown' ) ;
176
+ } ;
177
+
178
+ var findMatches = function ( element ) {
179
+ return findDropDown ( element ) . find ( 'li' ) ;
180
+ } ;
181
+
182
+ var triggerKeyDown = function ( element , keyCode ) {
183
+ var inputEl = findInput ( element ) ;
184
+ var e = $ . Event ( "keydown" ) ;
185
+ e . which = keyCode ;
186
+ inputEl . trigger ( e ) ;
187
+ } ;
188
+
189
+ //custom matchers
190
+ beforeEach ( function ( ) {
191
+ this . addMatchers ( {
192
+ toBeClosed : function ( ) {
193
+ var typeaheadEl = findDropDown ( this . actual ) ;
194
+ this . message = function ( ) {
195
+ return "Expected '" + angular . mock . dump ( this . actual ) + "' to be closed." ;
196
+ } ;
197
+ return ! typeaheadEl . hasClass ( 'open' ) && findMatches ( this . actual ) . length === 0 ;
198
+
199
+ } , toBeOpenWithActive : function ( noOfMatches , activeIdx ) {
200
+
201
+ var typeaheadEl = findDropDown ( this . actual ) ;
202
+ var liEls = findMatches ( this . actual ) ;
203
+
204
+ this . message = function ( ) {
205
+ return "Expected '" + angular . mock . dump ( this . actual ) + "' to be opened." ;
206
+ } ;
207
+ return typeaheadEl . hasClass ( 'open' ) && liEls . length === noOfMatches && $ ( liEls [ activeIdx ] ) . hasClass ( 'active' ) ;
208
+ }
209
+ } ) ;
210
+ } ) ;
211
+
212
+ //coarse grained, "integration" tests
213
+ describe ( 'initial state and model changes' , function ( ) {
214
+
215
+ it ( 'should be closed by default' , function ( ) {
216
+ var element = prepareInputEl ( "<div><input ng-model='result' typeahead='item for item in source'></div>" ) ;
217
+ expect ( element ) . toBeClosed ( ) ;
218
+ } ) ;
219
+
220
+ it ( 'should not get open on model change' , function ( ) {
221
+ var element = prepareInputEl ( "<div><input ng-model='result' typeahead='item for item in source'></div>" ) ;
222
+ $scope . $apply ( function ( ) {
223
+ $scope . result = 'foo' ;
224
+ } ) ;
225
+ expect ( element ) . toBeClosed ( ) ;
226
+ } ) ;
227
+ } ) ;
228
+
229
+ describe ( 'basic functionality' , function ( ) {
230
+
231
+ it ( 'should open and close typeahead based on matches' , function ( ) {
232
+ var element = prepareInputEl ( "<div><input ng-model='result' typeahead='item for item in source | filter:$viewValue'></div>" ) ;
233
+ changeInputValueTo ( element , 'ba' ) ;
234
+ expect ( element ) . toBeOpenWithActive ( 2 , 0 ) ;
235
+ } ) ;
236
+
237
+ it ( 'should not open typeahead if input value smaller than a defined threshold' , function ( ) {
238
+ var element = prepareInputEl ( "<div><input ng-model='result' typeahead='item for item in source | filter:$viewValue' typeahead-min-length='2'></div>" ) ;
239
+ changeInputValueTo ( element , 'b' ) ;
240
+ expect ( element ) . toBeClosed ( ) ;
241
+ } ) ;
242
+
243
+ it ( 'should support custom model selecting function' , function ( ) {
244
+ $scope . updaterFn = function ( selectedItem ) {
245
+ return 'prefix' + selectedItem ;
246
+ } ;
247
+ var element = prepareInputEl ( "<div><input ng-model='result' typeahead='updaterFn(item) as item for item in source | filter:$viewValue'></div>" ) ;
248
+ changeInputValueTo ( element , 'f' ) ;
249
+ triggerKeyDown ( element , 13 ) ;
250
+ expect ( $scope . result ) . toEqual ( 'prefixfoo' ) ;
251
+ } ) ;
252
+
253
+ it ( 'should support custom label rendering function' , function ( ) {
254
+ $scope . formatterFn = function ( sourceItem ) {
255
+ return 'prefix' + sourceItem ;
256
+ } ;
257
+
258
+ var element = prepareInputEl ( "<div><input ng-model='result' typeahead='item as formatterFn(item) for item in source | filter:$viewValue'></div>" ) ;
259
+ changeInputValueTo ( element , 'fo' ) ;
260
+ var matchHighlight = findMatches ( element ) . find ( 'a' ) . html ( ) ;
261
+ expect ( matchHighlight ) . toEqual ( 'prefix<strong>fo</strong>o' ) ;
262
+ } ) ;
263
+
264
+ } ) ;
265
+
266
+ describe ( 'selecting a match' , function ( ) {
267
+
268
+ it ( 'should select a match on enter' , function ( ) {
269
+
270
+ var element = prepareInputEl ( "<div><input ng-model='result' typeahead='item for item in source | filter:$viewValue'></div>" ) ;
271
+ var inputEl = findInput ( element ) ;
272
+
273
+ changeInputValueTo ( element , 'b' ) ;
274
+ triggerKeyDown ( element , 13 ) ;
275
+
276
+ expect ( $scope . result ) . toEqual ( 'bar' ) ;
277
+ expect ( inputEl . val ( ) ) . toEqual ( 'bar' ) ;
278
+ } ) ;
279
+
280
+ it ( 'should select a match on tab' , function ( ) {
281
+
282
+ var element = prepareInputEl ( "<div><input ng-model='result' typeahead='item for item in source | filter:$viewValue'></div>" ) ;
283
+ var inputEl = findInput ( element ) ;
284
+
285
+ changeInputValueTo ( element , 'b' ) ;
286
+ triggerKeyDown ( element , 9 ) ;
287
+
288
+ expect ( $scope . result ) . toEqual ( 'bar' ) ;
289
+ expect ( inputEl . val ( ) ) . toEqual ( 'bar' ) ;
290
+ } ) ;
291
+
292
+ it ( 'should select match on click' , function ( ) {
293
+
294
+ var element = prepareInputEl ( "<div><input ng-model='result' typeahead='item for item in source | filter:$viewValue'></div>" ) ;
295
+ var inputEl = findInput ( element ) ;
296
+
297
+ changeInputValueTo ( element , 'b' ) ;
298
+ var match = $ ( findMatches ( element ) [ 1 ] ) . find ( 'a' ) [ 0 ] ;
299
+
300
+ $ ( match ) . click ( ) ;
301
+ $scope . $digest ( ) ;
302
+
303
+ expect ( $scope . result ) . toEqual ( 'baz' ) ;
304
+ expect ( inputEl . val ( ) ) . toEqual ( 'baz' ) ;
305
+ } ) ;
306
+ } ) ;
307
+
308
+ } ) ;
309
+ } ) ;
0 commit comments