1
1
import { async , ComponentFixture , TestBed , inject } from '@angular/core/testing' ;
2
- import { Component , Renderer } from '@angular/core' ;
2
+ import { Component , Renderer , ViewChild } from '@angular/core' ;
3
3
import { StyleModule } from './index' ;
4
4
import { By } from '@angular/platform-browser' ;
5
5
import { TAB } from '../keyboard/keycodes' ;
6
- import { FocusOriginMonitor } from './focus-classes' ;
6
+ import { FocusOriginMonitor , FocusOrigin , CdkFocusClasses } from './focus-classes' ;
7
7
8
8
describe ( 'FocusOriginMonitor' , ( ) => {
9
9
let fixture : ComponentFixture < PlainButton > ;
10
10
let buttonElement : HTMLElement ;
11
11
let buttonRenderer : Renderer ;
12
12
let focusOriginMonitor : FocusOriginMonitor ;
13
+ let changeHandler : ( origin : FocusOrigin ) => void ;
13
14
14
15
beforeEach ( async ( ( ) => {
15
16
TestBed . configureTestingModule ( {
@@ -30,7 +31,9 @@ describe('FocusOriginMonitor', () => {
30
31
buttonRenderer = fixture . componentInstance . renderer ;
31
32
focusOriginMonitor = fom ;
32
33
33
- focusOriginMonitor . registerElementForFocusClasses ( buttonElement , buttonRenderer ) ;
34
+ changeHandler = jasmine . createSpy ( 'focus origin change handler' ) ;
35
+ focusOriginMonitor . registerElementForFocusClasses ( buttonElement , buttonRenderer )
36
+ . subscribe ( changeHandler ) ;
34
37
35
38
// Patch the element focus to properly emit focus events when the browser is blurred.
36
39
patchElementFocus ( buttonElement ) ;
@@ -45,6 +48,7 @@ describe('FocusOriginMonitor', () => {
45
48
46
49
expect ( buttonElement . classList . contains ( 'cdk-focused' ) )
47
50
. toBe ( true , 'button should have cdk-focused class' ) ;
51
+ expect ( changeHandler ) . toHaveBeenCalledTimes ( 1 ) ;
48
52
} , 0 ) ;
49
53
} ) ) ;
50
54
@@ -63,6 +67,7 @@ describe('FocusOriginMonitor', () => {
63
67
. toBe ( true , 'button should have cdk-focused class' ) ;
64
68
expect ( buttonElement . classList . contains ( 'cdk-keyboard-focused' ) )
65
69
. toBe ( true , 'button should have cdk-keyboard-focused class' ) ;
70
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'keyboard' ) ;
66
71
} , 0 ) ;
67
72
} ) ) ;
68
73
@@ -81,6 +86,7 @@ describe('FocusOriginMonitor', () => {
81
86
. toBe ( true , 'button should have cdk-focused class' ) ;
82
87
expect ( buttonElement . classList . contains ( 'cdk-mouse-focused' ) )
83
88
. toBe ( true , 'button should have cdk-mouse-focused class' ) ;
89
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'mouse' ) ;
84
90
} , 0 ) ;
85
91
} ) ) ;
86
92
@@ -98,6 +104,7 @@ describe('FocusOriginMonitor', () => {
98
104
. toBe ( true , 'button should have cdk-focused class' ) ;
99
105
expect ( buttonElement . classList . contains ( 'cdk-program-focused' ) )
100
106
. toBe ( true , 'button should have cdk-program-focused class' ) ;
107
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'program' ) ;
101
108
} , 0 ) ;
102
109
} ) ) ;
103
110
@@ -114,6 +121,7 @@ describe('FocusOriginMonitor', () => {
114
121
. toBe ( true , 'button should have cdk-focused class' ) ;
115
122
expect ( buttonElement . classList . contains ( 'cdk-keyboard-focused' ) )
116
123
. toBe ( true , 'button should have cdk-keyboard-focused class' ) ;
124
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'keyboard' ) ;
117
125
} , 0 ) ;
118
126
} ) ) ;
119
127
@@ -130,6 +138,7 @@ describe('FocusOriginMonitor', () => {
130
138
. toBe ( true , 'button should have cdk-focused class' ) ;
131
139
expect ( buttonElement . classList . contains ( 'cdk-mouse-focused' ) )
132
140
. toBe ( true , 'button should have cdk-mouse-focused class' ) ;
141
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'mouse' ) ;
133
142
} , 0 ) ;
134
143
} ) ) ;
135
144
@@ -146,6 +155,27 @@ describe('FocusOriginMonitor', () => {
146
155
. toBe ( true , 'button should have cdk-focused class' ) ;
147
156
expect ( buttonElement . classList . contains ( 'cdk-program-focused' ) )
148
157
. toBe ( true , 'button should have cdk-program-focused class' ) ;
158
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'program' ) ;
159
+ } , 0 ) ;
160
+ } ) ) ;
161
+
162
+ it ( 'should remove focus classes on blur' , async ( ( ) => {
163
+ buttonElement . focus ( ) ;
164
+ fixture . detectChanges ( ) ;
165
+
166
+ setTimeout ( ( ) => {
167
+ fixture . detectChanges ( ) ;
168
+
169
+ expect ( buttonElement . classList . length )
170
+ . toBe ( 2 , 'button should have exactly 2 focus classes' ) ;
171
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'program' ) ;
172
+
173
+ buttonElement . blur ( ) ;
174
+ fixture . detectChanges ( ) ;
175
+
176
+ expect ( buttonElement . classList . length )
177
+ . toBe ( 0 , 'button should not have any focus classes' ) ;
178
+ expect ( changeHandler ) . toHaveBeenCalledWith ( null ) ;
149
179
} , 0 ) ;
150
180
} ) ) ;
151
181
} ) ;
@@ -154,6 +184,7 @@ describe('FocusOriginMonitor', () => {
154
184
describe ( 'cdkFocusClasses' , ( ) => {
155
185
let fixture : ComponentFixture < ButtonWithFocusClasses > ;
156
186
let buttonElement : HTMLElement ;
187
+ let changeHandler : ( origin : FocusOrigin ) => void ;
157
188
158
189
beforeEach ( async ( ( ) => {
159
190
TestBed . configureTestingModule ( {
@@ -170,6 +201,8 @@ describe('cdkFocusClasses', () => {
170
201
fixture = TestBed . createComponent ( ButtonWithFocusClasses ) ;
171
202
fixture . detectChanges ( ) ;
172
203
204
+ changeHandler = jasmine . createSpy ( 'focus origin change handler' ) ;
205
+ fixture . componentInstance . cdkFocusClasses . changes . subscribe ( changeHandler ) ;
173
206
buttonElement = fixture . debugElement . query ( By . css ( 'button' ) ) . nativeElement ;
174
207
175
208
// Patch the element focus to properly emit focus events when the browser is blurred.
@@ -195,6 +228,7 @@ describe('cdkFocusClasses', () => {
195
228
. toBe ( true , 'button should have cdk-focused class' ) ;
196
229
expect ( buttonElement . classList . contains ( 'cdk-keyboard-focused' ) )
197
230
. toBe ( true , 'button should have cdk-keyboard-focused class' ) ;
231
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'keyboard' ) ;
198
232
} , 0 ) ;
199
233
} ) ) ;
200
234
@@ -213,6 +247,7 @@ describe('cdkFocusClasses', () => {
213
247
. toBe ( true , 'button should have cdk-focused class' ) ;
214
248
expect ( buttonElement . classList . contains ( 'cdk-mouse-focused' ) )
215
249
. toBe ( true , 'button should have cdk-mouse-focused class' ) ;
250
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'mouse' ) ;
216
251
} , 0 ) ;
217
252
} ) ) ;
218
253
@@ -230,6 +265,27 @@ describe('cdkFocusClasses', () => {
230
265
. toBe ( true , 'button should have cdk-focused class' ) ;
231
266
expect ( buttonElement . classList . contains ( 'cdk-program-focused' ) )
232
267
. toBe ( true , 'button should have cdk-program-focused class' ) ;
268
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'program' ) ;
269
+ } , 0 ) ;
270
+ } ) ) ;
271
+
272
+ it ( 'should remove focus classes on blur' , async ( ( ) => {
273
+ buttonElement . focus ( ) ;
274
+ fixture . detectChanges ( ) ;
275
+
276
+ setTimeout ( ( ) => {
277
+ fixture . detectChanges ( ) ;
278
+
279
+ expect ( buttonElement . classList . length )
280
+ . toBe ( 2 , 'button should have exactly 2 focus classes' ) ;
281
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'program' ) ;
282
+
283
+ buttonElement . blur ( ) ;
284
+ fixture . detectChanges ( ) ;
285
+
286
+ expect ( buttonElement . classList . length )
287
+ . toBe ( 0 , 'button should not have any focus classes' ) ;
288
+ expect ( changeHandler ) . toHaveBeenCalledWith ( null ) ;
233
289
} , 0 ) ;
234
290
} ) ) ;
235
291
} ) ;
@@ -242,7 +298,9 @@ class PlainButton {
242
298
243
299
244
300
@Component ( { template : `<button cdkFocusClasses>focus me!</button>` } )
245
- class ButtonWithFocusClasses { }
301
+ class ButtonWithFocusClasses {
302
+ @ViewChild ( CdkFocusClasses ) cdkFocusClasses : CdkFocusClasses ;
303
+ }
246
304
247
305
// TODO(devversion): move helper functions into a global utility file. See #2902
248
306
@@ -273,14 +331,21 @@ function dispatchFocusEvent(element: Node, type = 'focus') {
273
331
element . dispatchEvent ( event ) ;
274
332
}
275
333
276
- /** Patches an elements focus method to properly emit focus events when the browser is blurred. */
334
+ /**
335
+ * Patches an elements focus and blur methods to properly emit focus events when the browser is
336
+ * blurred.
337
+ */
277
338
function patchElementFocus ( element : HTMLElement ) {
278
339
// On Saucelabs, browsers will run simultaneously and therefore can't focus all browser windows
279
340
// at the same time. This is problematic when testing focus states. Chrome and Firefox
280
341
// only fire FocusEvents when the window is focused. This issue also appears locally.
281
342
let _nativeButtonFocus = element . focus . bind ( element ) ;
343
+ let _nativeButtonBlur = element . blur . bind ( element ) ;
282
344
283
345
element . focus = ( ) => {
284
346
document . hasFocus ( ) ? _nativeButtonFocus ( ) : dispatchFocusEvent ( element ) ;
285
347
} ;
348
+ element . blur = ( ) => {
349
+ document . hasFocus ( ) ? _nativeButtonBlur ( ) : dispatchFocusEvent ( element , 'blur' ) ;
350
+ } ;
286
351
}
0 commit comments