Skip to content

Commit fd01f7b

Browse files
cartantbenlesh
authored andcommitted
fix(find): unsubscribe from source when found (#3968)
* test(find): add failing unsubscribe test * test(findIndex): add failing unsubscribe test * test(first): add unsubscription test * fix(find): unsubscribe from source when found * chore(test): remove any
1 parent 6d6d08f commit fd01f7b

4 files changed

Lines changed: 79 additions & 27 deletions

File tree

‎spec/operators/find-spec.ts‎

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import{expect}from'chai';
2-
import{find,mergeMap}from'rxjs/operators';
2+
import{find,mergeMap,delay}from'rxjs/operators';
3+
import{TestScheduler}from'rxjs/testing';
34
import{hot,cold,expectObservable,expectSubscriptions}from'../helpers/marble-testing';
45
import{of,Observable,from}from'rxjs';
56

67
declarefunctionasDiagram(arg: string): Function;
78

9+
declareconstrxTestScheduler: TestScheduler;
10+
811
/** @test {find} */
912
describe('find operator',()=>{
1013
functiontruePredicate(x: any){
@@ -19,13 +22,13 @@ describe('find operator', () => {
1922

2023
constpredicate=function(x: number){returnx%5===0;};
2124

22-
expectObservable((<any>source).pipe(find(predicate))).toBe(expected,values);
25+
expectObservable(source.pipe(find(predicate))).toBe(expected,values);
2326
expectSubscriptions(source.subscriptions).toBe(subs);
2427
});
2528

2629
it('should throw if not provided a function',()=>{
2730
expect(()=>{
28-
(<any>of('yut','yee','sam')).pipe(find('yee'asany));
31+
of('yut','yee','sam').pipe(find('yee'asany));
2932
}).to.throw(TypeError,'predicate is not a function');
3033
});
3134

@@ -34,7 +37,7 @@ describe('find operator', () => {
3437
constsubs='^';
3538
constexpected='-';
3639

37-
expectObservable((<any>source).pipe(find(truePredicate))).toBe(expected);
40+
expectObservable(source.pipe(find(truePredicate))).toBe(expected);
3841
expectSubscriptions(source.subscriptions).toBe(subs);
3942
});
4043

@@ -43,7 +46,7 @@ describe('find operator', () => {
4346
constsubs='(^!)';
4447
constexpected='(x|)';
4548

46-
constresult=(<any>source).pipe(find(truePredicate));
49+
constresult=source.pipe(find(truePredicate));
4750

4851
expectObservable(result).toBe(expected,{x: undefined});
4952
expectSubscriptions(source.subscriptions).toBe(subs);
@@ -58,7 +61,7 @@ describe('find operator', () => {
5861
returnvalue==='a';
5962
};
6063

61-
expectObservable((<any>source).pipe(find(predicate))).toBe(expected);
64+
expectObservable(source.pipe(find(predicate))).toBe(expected);
6265
expectSubscriptions(source.subscriptions).toBe(subs);
6366
});
6467

@@ -71,7 +74,7 @@ describe('find operator', () => {
7174
returnvalue==='b';
7275
};
7376

74-
expectObservable((<any>source).pipe(find(predicate))).toBe(expected);
77+
expectObservable(source.pipe(find(predicate))).toBe(expected);
7578
expectSubscriptions(source.subscriptions).toBe(subs);
7679
});
7780

@@ -87,7 +90,7 @@ describe('find operator', () => {
8790
returnvalue===this.target;
8891
};
8992

90-
expectObservable((<any>source).pipe(find(predicate,finder))).toBe(expected);
93+
expectObservable(source.pipe(find(predicate,finder))).toBe(expected);
9194
expectSubscriptions(source.subscriptions).toBe(subs);
9295
});
9396

@@ -100,7 +103,7 @@ describe('find operator', () => {
100103
returnvalue==='z';
101104
};
102105

103-
expectObservable((<any>source).pipe(find(predicate))).toBe(expected,{x: undefined});
106+
expectObservable(source.pipe(find(predicate))).toBe(expected,{x: undefined});
104107
expectSubscriptions(source.subscriptions).toBe(subs);
105108
});
106109

@@ -110,7 +113,7 @@ describe('find operator', () => {
110113
constexpected='------- ';
111114
constunsub=' ! ';
112115

113-
constresult=(<any>source).pipe(find((value: string)=>value==='z'));
116+
constresult=source.pipe(find((value: string)=>value==='z'));
114117

115118
expectObservable(result,unsub).toBe(expected);
116119
expectSubscriptions(source.subscriptions).toBe(subs);
@@ -122,7 +125,7 @@ describe('find operator', () => {
122125
constexpected='------- ';
123126
constunsub=' ! ';
124127

125-
constresult=(<any>source).pipe(
128+
constresult=source.pipe(
126129
mergeMap((x: string)=>of(x)),
127130
find((value: string)=>value==='z'),
128131
mergeMap((x: string)=>of(x))
@@ -132,6 +135,20 @@ describe('find operator', () => {
132135
expectSubscriptions(source.subscriptions).toBe(subs);
133136
});
134137

138+
it('should unsubscribe when the predicate is matched',()=>{
139+
constsource=hot('--a--b---c-|');
140+
constsubs='^ !';
141+
constexpected='-------(b|)';
142+
143+
constduration=rxTestScheduler.createTime('--|');
144+
145+
expectObservable(source.pipe(
146+
find((value: string)=>value==='b'),
147+
delay(duration,rxTestScheduler)
148+
)).toBe(expected);
149+
expectSubscriptions(source.subscriptions).toBe(subs);
150+
});
151+
135152
it('should raise if source raise error while element does not match with predicate',()=>{
136153
constsource=hot('--a--b--#');
137154
constsubs='^ !';
@@ -141,7 +158,7 @@ describe('find operator', () => {
141158
returnvalue==='z';
142159
};
143160

144-
expectObservable((<any>source).pipe(find(predicate))).toBe(expected);
161+
expectObservable(source.pipe(find(predicate))).toBe(expected);
145162
expectSubscriptions(source.subscriptions).toBe(subs);
146163
});
147164

@@ -154,7 +171,7 @@ describe('find operator', () => {
154171
throw'error';
155172
};
156173

157-
expectObservable((<any>source).pipe(find(predicate))).toBe(expected);
174+
expectObservable(source.pipe(find(predicate))).toBe(expected);
158175
expectSubscriptions(source.subscriptions).toBe(subs);
159176
});
160177

‎spec/operators/findIndex-spec.ts‎

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import{findIndex,mergeMap}from'rxjs/operators';
1+
import{findIndex,mergeMap,delay}from'rxjs/operators';
2+
import{TestScheduler}from'rxjs/testing';
23
import{hot,cold,expectObservable,expectSubscriptions}from'../helpers/marble-testing';
34
import{of}from'rxjs';
45

56
declarefunctionasDiagram(arg: string): Function;
67

8+
declareconstrxTestScheduler: TestScheduler;
9+
710
/** @test {findIndex} */
811
describe('findIndex operator',()=>{
912
functiontruePredicate(x: any){
@@ -18,7 +21,7 @@ describe('findIndex operator', () => {
1821

1922
constpredicate=function(x: number){returnx%5===0;};
2023

21-
expectObservable((<any>source).pipe(findIndex(predicate))).toBe(expected,{x: 2});
24+
expectObservable(source.pipe(findIndex(predicate))).toBe(expected,{x: 2});
2225
expectSubscriptions(source.subscriptions).toBe(subs);
2326
});
2427

@@ -27,7 +30,7 @@ describe('findIndex operator', () => {
2730
constsubs='^';
2831
constexpected='-';
2932

30-
expectObservable((<any>source).pipe(findIndex(truePredicate))).toBe(expected);
33+
expectObservable(source.pipe(findIndex(truePredicate))).toBe(expected);
3134
expectSubscriptions(source.subscriptions).toBe(subs);
3235
});
3336

@@ -36,7 +39,7 @@ describe('findIndex operator', () => {
3639
constsubs='(^!)';
3740
constexpected='(x|)';
3841

39-
constresult=(<any>source).pipe(findIndex(truePredicate));
42+
constresult=source.pipe(findIndex(truePredicate));
4043

4144
expectObservable(result).toBe(expected,{x: -1});
4245
expectSubscriptions(source.subscriptions).toBe(subs);
@@ -52,7 +55,7 @@ describe('findIndex operator', () => {
5255
returnvalue===sourceValue;
5356
};
5457

55-
expectObservable((<any>source).pipe(findIndex(predicate))).toBe(expected,{x: 0});
58+
expectObservable(source.pipe(findIndex(predicate))).toBe(expected,{x: 0});
5659
expectSubscriptions(source.subscriptions).toBe(subs);
5760
});
5861

@@ -65,7 +68,7 @@ describe('findIndex operator', () => {
6568
returnvalue===7;
6669
};
6770

68-
expectObservable((<any>source).pipe(findIndex(predicate))).toBe(expected,{x: 1});
71+
expectObservable(source.pipe(findIndex(predicate))).toBe(expected,{x: 1});
6972
expectSubscriptions(source.subscriptions).toBe(subs);
7073
});
7174

@@ -78,7 +81,7 @@ describe('findIndex operator', () => {
7881
constpredicate=function(this: typeofsourceValues,value: number){
7982
returnvalue===this.b;
8083
};
81-
constresult=(<any>source).pipe(findIndex(predicate,sourceValues));
84+
constresult=source.pipe(findIndex(predicate,sourceValues));
8285

8386
expectObservable(result).toBe(expected,{x: 1});
8487
expectSubscriptions(source.subscriptions).toBe(subs);
@@ -93,7 +96,7 @@ describe('findIndex operator', () => {
9396
returnvalue==='z';
9497
};
9598

96-
expectObservable((<any>source).pipe(findIndex(predicate))).toBe(expected,{x: -1});
99+
expectObservable(source.pipe(findIndex(predicate))).toBe(expected,{x: -1});
97100
expectSubscriptions(source.subscriptions).toBe(subs);
98101
});
99102

@@ -103,7 +106,7 @@ describe('findIndex operator', () => {
103106
constexpected='------- ';
104107
constunsub=' ! ';
105108

106-
constresult=(<any>source).pipe(findIndex((value: string)=>value==='z'));
109+
constresult=source.pipe(findIndex((value: string)=>value==='z'));
107110

108111
expectObservable(result,unsub).toBe(expected);
109112
expectSubscriptions(source.subscriptions).toBe(subs);
@@ -115,16 +118,30 @@ describe('findIndex operator', () => {
115118
constexpected='------- ';
116119
constunsub=' ! ';
117120

118-
constresult=(<any>source).pipe(
121+
constresult=source.pipe(
119122
mergeMap((x: string)=>of(x)),
120123
findIndex((value: string)=>value==='z'),
121-
mergeMap((x: string)=>of(x))
124+
mergeMap((x: number)=>of(x))
122125
);
123126

124127
expectObservable(result,unsub).toBe(expected);
125128
expectSubscriptions(source.subscriptions).toBe(subs);
126129
});
127130

131+
it('should unsubscribe when the predicate is matched',()=>{
132+
constsource=hot('--a--b---c-|');
133+
constsubs='^ !';
134+
constexpected='-------(x|)';
135+
136+
constduration=rxTestScheduler.createTime('--|');
137+
138+
expectObservable(source.pipe(
139+
findIndex((value: string)=>value==='b'),
140+
delay(duration,rxTestScheduler)
141+
)).toBe(expected,{x: 1});
142+
expectSubscriptions(source.subscriptions).toBe(subs);
143+
});
144+
128145
it('should raise if source raise error while element does not match with predicate',()=>{
129146
constsource=hot('--a--b--#');
130147
constsubs='^ !';
@@ -134,7 +151,7 @@ describe('findIndex operator', () => {
134151
returnvalue==='z';
135152
};
136153

137-
expectObservable((<any>source).pipe(findIndex(predicate))).toBe(expected);
154+
expectObservable(source.pipe(findIndex(predicate))).toBe(expected);
138155
expectSubscriptions(source.subscriptions).toBe(subs);
139156
});
140157

@@ -147,7 +164,7 @@ describe('findIndex operator', () => {
147164
throw'error';
148165
};
149166

150-
expectObservable((<any>source).pipe(findIndex(predicate))).toBe(expected);
167+
expectObservable(source.pipe(findIndex(predicate))).toBe(expected);
151168
expectSubscriptions(source.subscriptions).toBe(subs);
152169
});
153170
});

‎spec/operators/first-spec.ts‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import{expect}from'chai';
22
import{hot,expectObservable,expectSubscriptions}from'../helpers/marble-testing';
3-
import{first,mergeMap}from'rxjs/operators';
3+
import{first,mergeMap,delay}from'rxjs/operators';
4+
import{TestScheduler}from'rxjs/testing';
45
import{of,from,Observable,Subject,EmptyError}from'rxjs';
56

67
declarefunctionasDiagram(arg: string): Function;
78

9+
declareconstrxTestScheduler: TestScheduler;
10+
811
/** @test {first} */
912
describe('Observable.prototype.first',()=>{
1013
asDiagram('first')('should take the first value of an observable with many values',()=>{
@@ -101,6 +104,20 @@ describe('Observable.prototype.first', () => {
101104
expectSubscriptions(e1.subscriptions).toBe(e1subs);
102105
});
103106

107+
it('should unsubscribe when the first value is receiv',()=>{
108+
constsource=hot('--a--b---c-|');
109+
constsubs='^ !';
110+
constexpected='----(a|)';
111+
112+
constduration=rxTestScheduler.createTime('--|');
113+
114+
expectObservable(source.pipe(
115+
first(),
116+
delay(duration,rxTestScheduler)
117+
)).toBe(expected);
118+
expectSubscriptions(source.subscriptions).toBe(subs);
119+
});
120+
104121
it('should return first value that matches a predicate',()=>{
105122
conste1=hot('--a-^--b--c--a--c--|');
106123
constexpected='------(c|)';

‎src/internal/operators/find.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export class FindValueSubscriber<T> extends Subscriber<T> {
8484

8585
destination.next(value);
8686
destination.complete();
87+
this.unsubscribe();
8788
}
8889

8990
protected_next(value: T): void{

0 commit comments

Comments
 (0)