@@ -11,9 +11,11 @@ import {BreakpointObserver, BreakpointState} from './breakpoints-observer';
11
11
import { MediaMatcher } from './media-matcher' ;
12
12
import { fakeAsync , TestBed , inject , flush } from '@angular/core/testing' ;
13
13
import { Injectable } from '@angular/core' ;
14
+ import { Subscription } from 'rxjs' ;
15
+ import { take } from 'rxjs/operators' ;
14
16
15
17
describe ( 'BreakpointObserver' , ( ) => {
16
- let breakpointManager : BreakpointObserver ;
18
+ let breakpointObserver : BreakpointObserver ;
17
19
let mediaMatcher : FakeMediaMatcher ;
18
20
19
21
beforeEach ( fakeAsync ( ( ) => {
@@ -26,7 +28,7 @@ describe('BreakpointObserver', () => {
26
28
beforeEach ( inject (
27
29
[ BreakpointObserver , MediaMatcher ] ,
28
30
( bm : BreakpointObserver , mm : FakeMediaMatcher ) => {
29
- breakpointManager = bm ;
31
+ breakpointObserver = bm ;
30
32
mediaMatcher = mm ;
31
33
} ) ) ;
32
34
@@ -36,48 +38,48 @@ describe('BreakpointObserver', () => {
36
38
37
39
it ( 'retrieves the whether a query is currently matched' , fakeAsync ( ( ) => {
38
40
const query = 'everything starts as true in the FakeMediaMatcher' ;
39
- expect ( breakpointManager . isMatched ( query ) ) . toBeTruthy ( ) ;
41
+ expect ( breakpointObserver . isMatched ( query ) ) . toBeTruthy ( ) ;
40
42
} ) ) ;
41
43
42
44
it ( 'reuses the same MediaQueryList for matching queries' , fakeAsync ( ( ) => {
43
45
expect ( mediaMatcher . queryCount ) . toBe ( 0 ) ;
44
- breakpointManager . observe ( 'query1' ) ;
46
+ breakpointObserver . observe ( 'query1' ) ;
45
47
expect ( mediaMatcher . queryCount ) . toBe ( 1 ) ;
46
- breakpointManager . observe ( 'query1' ) ;
48
+ breakpointObserver . observe ( 'query1' ) ;
47
49
expect ( mediaMatcher . queryCount ) . toBe ( 1 ) ;
48
- breakpointManager . observe ( 'query2' ) ;
50
+ breakpointObserver . observe ( 'query2' ) ;
49
51
expect ( mediaMatcher . queryCount ) . toBe ( 2 ) ;
50
- breakpointManager . observe ( 'query1' ) ;
52
+ breakpointObserver . observe ( 'query1' ) ;
51
53
expect ( mediaMatcher . queryCount ) . toBe ( 2 ) ;
52
54
} ) ) ;
53
55
54
56
it ( 'splits combined query strings into individual matchMedia listeners' , fakeAsync ( ( ) => {
55
57
expect ( mediaMatcher . queryCount ) . toBe ( 0 ) ;
56
- breakpointManager . observe ( 'query1, query2' ) ;
58
+ breakpointObserver . observe ( 'query1, query2' ) ;
57
59
expect ( mediaMatcher . queryCount ) . toBe ( 2 ) ;
58
- breakpointManager . observe ( 'query1' ) ;
60
+ breakpointObserver . observe ( 'query1' ) ;
59
61
expect ( mediaMatcher . queryCount ) . toBe ( 2 ) ;
60
- breakpointManager . observe ( 'query2, query3' ) ;
62
+ breakpointObserver . observe ( 'query2, query3' ) ;
61
63
expect ( mediaMatcher . queryCount ) . toBe ( 3 ) ;
62
64
} ) ) ;
63
65
64
66
it ( 'accepts an array of queries' , fakeAsync ( ( ) => {
65
67
const queries = [ '1 query' , '2 query' , 'red query' , 'blue query' ] ;
66
- breakpointManager . observe ( queries ) ;
68
+ breakpointObserver . observe ( queries ) ;
67
69
expect ( mediaMatcher . queryCount ) . toBe ( queries . length ) ;
68
70
} ) ) ;
69
71
70
72
it ( 'completes all events when the breakpoint manager is destroyed' , fakeAsync ( ( ) => {
71
73
const firstTest = jasmine . createSpy ( 'test1' ) ;
72
- breakpointManager . observe ( 'test1' ) . subscribe ( undefined , undefined , firstTest ) ;
74
+ breakpointObserver . observe ( 'test1' ) . subscribe ( undefined , undefined , firstTest ) ;
73
75
const secondTest = jasmine . createSpy ( 'test2' ) ;
74
- breakpointManager . observe ( 'test2' ) . subscribe ( undefined , undefined , secondTest ) ;
76
+ breakpointObserver . observe ( 'test2' ) . subscribe ( undefined , undefined , secondTest ) ;
75
77
76
78
flush ( ) ;
77
79
expect ( firstTest ) . not . toHaveBeenCalled ( ) ;
78
80
expect ( secondTest ) . not . toHaveBeenCalled ( ) ;
79
81
80
- breakpointManager . ngOnDestroy ( ) ;
82
+ breakpointObserver . ngOnDestroy ( ) ;
81
83
flush ( ) ;
82
84
83
85
expect ( firstTest ) . toHaveBeenCalled ( ) ;
@@ -87,7 +89,7 @@ describe('BreakpointObserver', () => {
87
89
it ( 'emits an event on the observable when values change' , fakeAsync ( ( ) => {
88
90
const query = '(width: 999px)' ;
89
91
let queryMatchState = false ;
90
- breakpointManager . observe ( query ) . subscribe ( ( state : BreakpointState ) => {
92
+ breakpointObserver . observe ( query ) . subscribe ( ( state : BreakpointState ) => {
91
93
queryMatchState = state . matches ;
92
94
} ) ;
93
95
@@ -103,7 +105,7 @@ describe('BreakpointObserver', () => {
103
105
const queryOne = '(width: 999px)' ;
104
106
const queryTwo = '(width: 700px)' ;
105
107
let state : BreakpointState = { matches : false , breakpoints : { } } ;
106
- breakpointManager . observe ( [ queryOne , queryTwo ] ) . subscribe ( ( breakpoint : BreakpointState ) => {
108
+ breakpointObserver . observe ( [ queryOne , queryTwo ] ) . subscribe ( ( breakpoint : BreakpointState ) => {
107
109
state = breakpoint ;
108
110
} ) ;
109
111
@@ -120,38 +122,72 @@ describe('BreakpointObserver', () => {
120
122
121
123
it ( 'emits a true matches state when the query is matched' , fakeAsync ( ( ) => {
122
124
const query = '(width: 999px)' ;
123
- breakpointManager . observe ( query ) . subscribe ( ) ;
125
+ breakpointObserver . observe ( query ) . subscribe ( ) ;
124
126
mediaMatcher . setMatchesQuery ( query , true ) ;
125
- expect ( breakpointManager . isMatched ( query ) ) . toBeTruthy ( ) ;
127
+ expect ( breakpointObserver . isMatched ( query ) ) . toBeTruthy ( ) ;
126
128
} ) ) ;
127
129
128
130
it ( 'emits a false matches state when the query is not matched' , fakeAsync ( ( ) => {
129
131
const query = '(width: 999px)' ;
130
- breakpointManager . observe ( query ) . subscribe ( ) ;
132
+ breakpointObserver . observe ( query ) . subscribe ( ) ;
131
133
mediaMatcher . setMatchesQuery ( query , false ) ;
132
- expect ( breakpointManager . isMatched ( query ) ) . toBeFalsy ( ) ;
134
+ expect ( breakpointObserver . isMatched ( query ) ) . toBeFalsy ( ) ;
135
+ } ) ) ;
136
+
137
+ it ( 'should not complete other subscribers when preceding subscriber completes' , fakeAsync ( ( ) => {
138
+ const queryOne = '(width: 700px)' ;
139
+ const queryTwo = '(width: 999px)' ;
140
+ const breakpoint = breakpointObserver . observe ( [ queryOne , queryTwo ] ) ;
141
+ const subscriptions : Subscription [ ] = [ ] ;
142
+ let emittedValues : number [ ] = [ ] ;
143
+
144
+ subscriptions . push ( breakpoint . subscribe ( ( ) => emittedValues . push ( 1 ) ) ) ;
145
+ subscriptions . push ( breakpoint . pipe ( take ( 1 ) ) . subscribe ( ( ) => emittedValues . push ( 2 ) ) ) ;
146
+ subscriptions . push ( breakpoint . subscribe ( ( ) => emittedValues . push ( 3 ) ) ) ;
147
+ subscriptions . push ( breakpoint . subscribe ( ( ) => emittedValues . push ( 4 ) ) ) ;
148
+
149
+ mediaMatcher . setMatchesQuery ( queryOne , true ) ;
150
+ mediaMatcher . setMatchesQuery ( queryTwo , false ) ;
151
+ flush ( ) ;
152
+
153
+ expect ( emittedValues ) . toEqual ( [ 1 , 2 , 3 , 4 ] ) ;
154
+ emittedValues = [ ] ;
155
+
156
+ mediaMatcher . setMatchesQuery ( queryOne , false ) ;
157
+ mediaMatcher . setMatchesQuery ( queryTwo , true ) ;
158
+ flush ( ) ;
159
+
160
+ expect ( emittedValues ) . toEqual ( [ 1 , 3 , 4 ] ) ;
161
+
162
+ subscriptions . forEach ( subscription => subscription . unsubscribe ( ) ) ;
133
163
} ) ) ;
134
164
} ) ;
135
165
136
166
export class FakeMediaQueryList {
137
167
/** The callback for change events. */
138
- addListenerCallback ? : ( mql : MediaQueryListEvent ) => void ;
168
+ private _listeners : ( ( mql : MediaQueryListEvent ) => void ) [ ] = [ ] ;
139
169
140
170
constructor ( public matches : boolean , public media : string ) { }
141
171
142
172
/** Toggles the matches state and "emits" a change event. */
143
173
setMatches ( matches : boolean ) {
144
174
this . matches = matches ;
145
- this . addListenerCallback ! ( this as any ) ;
175
+ this . _listeners . forEach ( listener => listener ( this as any ) ) ;
146
176
}
147
177
148
- /** Registers the callback method for change events. */
178
+ /** Registers a callback method for change events. */
149
179
addListener ( callback : ( mql : MediaQueryListEvent ) => void ) {
150
- this . addListenerCallback = callback ;
180
+ this . _listeners . push ( callback ) ;
151
181
}
152
182
153
- // Noop removal method for testing.
154
- removeListener ( ) { }
183
+ /** Removes a callback method from the change events. */
184
+ removeListener ( callback : ( mql : MediaQueryListEvent ) => void ) {
185
+ const index = this . _listeners . indexOf ( callback ) ;
186
+
187
+ if ( index > - 1 ) {
188
+ this . _listeners . splice ( index , 1 ) ;
189
+ }
190
+ }
155
191
}
156
192
157
193
@Injectable ( )
0 commit comments