7
7
createMouseEvent ,
8
8
dispatchEvent ,
9
9
} from '@angular/cdk/testing/private' ;
10
+ import { DOCUMENT } from '@angular/common' ;
10
11
import { Component , NgZone } from '@angular/core' ;
11
12
import { ComponentFixture , fakeAsync , flush , inject , TestBed , tick } from '@angular/core/testing' ;
12
13
import { By } from '@angular/platform-browser' ;
@@ -25,13 +26,39 @@ describe('FocusMonitor', () => {
25
26
let buttonElement : HTMLElement ;
26
27
let focusMonitor : FocusMonitor ;
27
28
let changeHandler : ( origin : FocusOrigin ) => void ;
29
+ let fakeActiveElement : HTMLElement | null ;
28
30
29
31
beforeEach ( ( ) => {
32
+ fakeActiveElement = null ;
33
+
30
34
TestBed . configureTestingModule ( {
31
35
imports : [ A11yModule ] ,
32
- declarations : [
33
- PlainButton ,
34
- ] ,
36
+ declarations : [ PlainButton ] ,
37
+ providers : [ {
38
+ provide : DOCUMENT ,
39
+ useFactory : ( ) => {
40
+ // We have to stub out the `document` in order to be able to fake `activeElement`.
41
+ const fakeDocument = { body : document . body } ;
42
+
43
+ [
44
+ 'createElement' ,
45
+ 'dispatchEvent' ,
46
+ 'querySelectorAll' ,
47
+ 'addEventListener' ,
48
+ 'removeEventListener'
49
+ ] . forEach ( method => {
50
+ ( fakeDocument as any ) [ method ] = function ( ) {
51
+ return ( document as any ) [ method ] . apply ( document , arguments ) ;
52
+ } ;
53
+ } ) ;
54
+
55
+ Object . defineProperty ( fakeDocument , 'activeElement' , {
56
+ get : ( ) => fakeActiveElement || document . activeElement
57
+ } ) ;
58
+
59
+ return fakeDocument ;
60
+ }
61
+ } ]
35
62
} ) . compileComponents ( ) ;
36
63
} ) ;
37
64
@@ -294,6 +321,61 @@ describe('FocusMonitor', () => {
294
321
expect ( parent . classList ) . toContain ( 'cdk-mouse-focused' ) ;
295
322
} ) ) ;
296
323
324
+ it ( 'focusVia should change the focus origin when called on the focused node' , fakeAsync ( ( ) => {
325
+ spyOn ( buttonElement , 'focus' ) . and . callThrough ( ) ;
326
+ focusMonitor . focusVia ( buttonElement , 'keyboard' ) ;
327
+ flush ( ) ;
328
+ fakeActiveElement = buttonElement ;
329
+
330
+ expect ( buttonElement . classList . length )
331
+ . toBe ( 2 , 'button should have exactly 2 focus classes' ) ;
332
+ expect ( buttonElement . classList . contains ( 'cdk-focused' ) )
333
+ . toBe ( true , 'button should have cdk-focused class' ) ;
334
+ expect ( buttonElement . classList . contains ( 'cdk-keyboard-focused' ) )
335
+ . toBe ( true , 'button should have cdk-keyboard-focused class' ) ;
336
+ expect ( changeHandler ) . toHaveBeenCalledTimes ( 1 ) ;
337
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'keyboard' ) ;
338
+ expect ( buttonElement . focus ) . toHaveBeenCalledTimes ( 1 ) ;
339
+
340
+ focusMonitor . focusVia ( buttonElement , 'mouse' ) ;
341
+ flush ( ) ;
342
+ fakeActiveElement = buttonElement ;
343
+
344
+ expect ( buttonElement . classList . length )
345
+ . toBe ( 2 , 'button should have exactly 2 focus classes' ) ;
346
+ expect ( buttonElement . classList . contains ( 'cdk-focused' ) )
347
+ . toBe ( true , 'button should have cdk-focused class' ) ;
348
+ expect ( buttonElement . classList . contains ( 'cdk-mouse-focused' ) )
349
+ . toBe ( true , 'button should have cdk-mouse-focused class' ) ;
350
+ expect ( changeHandler ) . toHaveBeenCalledTimes ( 2 ) ;
351
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'mouse' ) ;
352
+ expect ( buttonElement . focus ) . toHaveBeenCalledTimes ( 1 ) ;
353
+ } ) ) ;
354
+
355
+ it ( 'focusVia should not change focus if focusing with the same origin' , fakeAsync ( ( ) => {
356
+ spyOn ( buttonElement , 'focus' ) . and . callThrough ( ) ;
357
+ focusMonitor . focusVia ( buttonElement , 'keyboard' ) ;
358
+ flush ( ) ;
359
+ fakeActiveElement = buttonElement ;
360
+
361
+ expect ( buttonElement . classList . length )
362
+ . toBe ( 2 , 'button should have exactly 2 focus classes' ) ;
363
+ expect ( buttonElement . classList . contains ( 'cdk-focused' ) )
364
+ . toBe ( true , 'button should have cdk-focused class' ) ;
365
+ expect ( buttonElement . classList . contains ( 'cdk-keyboard-focused' ) )
366
+ . toBe ( true , 'button should have cdk-keyboard-focused class' ) ;
367
+ expect ( changeHandler ) . toHaveBeenCalledTimes ( 1 ) ;
368
+ expect ( changeHandler ) . toHaveBeenCalledWith ( 'keyboard' ) ;
369
+ expect ( buttonElement . focus ) . toHaveBeenCalledTimes ( 1 ) ;
370
+
371
+ focusMonitor . focusVia ( buttonElement , 'keyboard' ) ;
372
+ flush ( ) ;
373
+ fakeActiveElement = buttonElement ;
374
+
375
+ expect ( changeHandler ) . toHaveBeenCalledTimes ( 1 ) ;
376
+ expect ( buttonElement . focus ) . toHaveBeenCalledTimes ( 1 ) ;
377
+ } ) ) ;
378
+
297
379
} ) ;
298
380
299
381
describe ( 'FocusMonitor with "eventual" detection' , ( ) => {
0 commit comments