-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix(take): add runtime argument checks BREAKING CHANGE: take will not throw runtime error for arguments that are negative or NaN, this includes non-TS calls like `take()`. * chore: update takeLast tests * fix(takeLast): add runtime assertions for invalid arguments BREAKING CHANGE: Calling takeLast without arguments or with an argument that is NaN will throw a TypeError
- Loading branch information
Showing
4 changed files
with
195 additions
and
133 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,164 +1,216 @@ | ||
import { expect } from 'chai'; | ||
import { hot, cold, expectObservable, expectSubscriptions } from '../helpers/marble-testing'; | ||
import { takeLast, mergeMap } from 'rxjs/operators'; | ||
import { range, ArgumentOutOfRangeError, of } from 'rxjs'; | ||
|
||
declare function asDiagram(arg: string): Function; | ||
import { TestScheduler } from 'rxjs/testing'; | ||
import { assertDeepEquals } from '../helpers/test-helper'; | ||
|
||
/** @test {takeLast} */ | ||
describe('takeLast operator', () => { | ||
asDiagram('takeLast(2)')('should take two values of an observable with many values', () => { | ||
const e1 = cold('--a-----b----c---d--| '); | ||
const e1subs = '^ ! '; | ||
const expected = '--------------------(cd|)'; | ||
let rxTest: TestScheduler; | ||
|
||
beforeEach(() => { | ||
rxTest = new TestScheduler(assertDeepEquals); | ||
}); | ||
|
||
it('should error for invalid arguments', () => { | ||
expect(() => { | ||
of(1, 2, 3).pipe((takeLast as any)()); | ||
}).to.throw(TypeError, `'count' is not a number`); | ||
|
||
expect(() => { | ||
of(1, 2, 3).pipe((takeLast as any)('banana')); | ||
}).to.throw(TypeError, `'count' is not a number`); | ||
|
||
expect(() => { | ||
of(1, 2, 3).pipe((takeLast as any)('3')); | ||
}).not.to.throw(); | ||
}); | ||
|
||
it('should take two values of an observable with many values', () => { | ||
rxTest.run(({ cold, expectObservable, expectSubscriptions }) => { | ||
const e1 = cold('--a-----b----c---d--| '); | ||
const e1subs = ' ^-------------------! '; | ||
const expected = '--------------------(cd|)'; | ||
|
||
expectObservable(e1.pipe(takeLast(2))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(2))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should take last three values', () => { | ||
const e1 = cold('--a-----b----c---d--| '); | ||
const e1subs = '^ ! '; | ||
const expected = '--------------------(bcd|)'; | ||
rxTest.run(({ cold, expectObservable, expectSubscriptions }) => { | ||
const e1 = cold(' --a-----b----c---d--| '); | ||
const e1subs = ' ^-------------------! '; | ||
const expected = '--------------------(bcd|)'; | ||
|
||
expectObservable(e1.pipe(takeLast(3))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(3))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should take all element when try to take larger then source', () => { | ||
const e1 = cold('--a-----b----c---d--| '); | ||
const e1subs = '^ ! '; | ||
const expected = '--------------------(abcd|)'; | ||
rxTest.run(({ cold, expectObservable, expectSubscriptions }) => { | ||
const e1 = cold(' --a-----b----c---d--| '); | ||
const e1subs = ' ^-------------------! '; | ||
const expected = '--------------------(abcd|)'; | ||
|
||
expectObservable(e1.pipe(takeLast(5))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(5))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should take all element when try to take exact', () => { | ||
const e1 = cold('--a-----b----c---d--| '); | ||
const e1subs = '^ ! '; | ||
const expected = '--------------------(abcd|)'; | ||
rxTest.run(({ cold, expectObservable, expectSubscriptions }) => { | ||
const e1 = cold(' --a-----b----c---d--| '); | ||
const e1subs = ' ^-------------------! '; | ||
const expected = '--------------------(abcd|)'; | ||
|
||
expectObservable(e1.pipe(takeLast(4))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(4))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should not take any values', () => { | ||
const e1 = cold('--a-----b----c---d--|'); | ||
const expected = '|'; | ||
rxTest.run(({ cold, expectObservable }) => { | ||
const e1 = cold(' --a-----b----c---d--|'); | ||
const expected = '|'; | ||
|
||
expectObservable(e1.pipe(takeLast(0))).toBe(expected); | ||
expectObservable(e1.pipe(takeLast(0))).toBe(expected); | ||
}); | ||
}); | ||
|
||
it('should work with empty', () => { | ||
const e1 = cold('|'); | ||
const e1subs = '(^!)'; | ||
const expected = '|'; | ||
rxTest.run(({ cold, expectObservable, expectSubscriptions }) => { | ||
const e1 = cold(' |'); | ||
const e1subs = ' (^!)'; | ||
const expected = '|'; | ||
|
||
expectObservable(e1.pipe(takeLast(42))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(42))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should go on forever on never', () => { | ||
const e1 = cold('-'); | ||
const e1subs = '^'; | ||
const expected = '-'; | ||
rxTest.run(({ cold, expectObservable, expectSubscriptions }) => { | ||
const e1 = cold(' -'); | ||
const e1subs = ' ^'; | ||
const expected = '-'; | ||
|
||
expectObservable(e1.pipe(takeLast(42))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(42))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should be empty on takeLast(0)', () => { | ||
const e1 = hot('--a--^--b----c---d--|'); | ||
const e1subs: string[] = []; // Don't subscribe at all | ||
const expected = '|'; | ||
rxTest.run(({ hot, expectObservable, expectSubscriptions }) => { | ||
const e1 = hot('--a--^--b----c---d--|'); | ||
const expected = ' |'; | ||
const e1subs: string[] = []; // Don't subscribe at all | ||
|
||
expectObservable(e1.pipe(takeLast(0))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(0))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should take one value from an observable with one value', () => { | ||
const e1 = hot('---(a|)'); | ||
const e1subs = '^ ! '; | ||
const expected = '---(a|)'; | ||
rxTest.run(({ hot, expectObservable, expectSubscriptions }) => { | ||
const e1 = hot(' ---(a|)'); | ||
const e1subs = ' ^--! '; | ||
const expected = '---(a|)'; | ||
|
||
expectObservable(e1.pipe(takeLast(1))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(1))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should take one value from an observable with many values', () => { | ||
const e1 = hot('--a--^--b----c---d--| '); | ||
const e1subs = '^ ! '; | ||
const expected = '---------------(d|)'; | ||
rxTest.run(({ hot, expectObservable, expectSubscriptions }) => { | ||
const e1 = hot('--a--^--b----c---d--| '); | ||
const e1subs = ' ^--------------! '; | ||
const expected = ' ---------------(d|)'; | ||
|
||
expectObservable(e1.pipe(takeLast(1))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(1))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should error on empty', () => { | ||
const e1 = hot('--a--^----|'); | ||
const e1subs = '^ !'; | ||
const expected = '-----|'; | ||
rxTest.run(({ hot, expectObservable, expectSubscriptions }) => { | ||
const e1 = hot('--a--^----|'); | ||
const e1subs = ' ^----!'; | ||
const expected = ' -----|'; | ||
|
||
expectObservable(e1.pipe(takeLast(42))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(42))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should propagate error from the source observable', () => { | ||
const e1 = hot('---^---#', undefined, 'too bad'); | ||
const e1subs = '^ !'; | ||
const expected = '----#'; | ||
rxTest.run(({ hot, expectObservable, expectSubscriptions }) => { | ||
const e1 = hot('---^---#', undefined, 'too bad'); | ||
const e1subs = ' ^---!'; | ||
const expected = ' ----#'; | ||
|
||
expectObservable(e1.pipe(takeLast(42))).toBe(expected, null, 'too bad'); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(42))).toBe(expected, null, 'too bad'); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should propagate error from an observable with values', () => { | ||
const e1 = hot('---^--a--b--#'); | ||
const e1subs = '^ !'; | ||
const expected = '---------#'; | ||
rxTest.run(({ hot, expectObservable, expectSubscriptions }) => { | ||
const e1 = hot('---^--a--b--#'); | ||
const e1subs = ' ^--------!'; | ||
const expected = ' ---------#'; | ||
|
||
expectObservable(e1.pipe(takeLast(42))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(42))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should allow unsubscribing explicitly and early', () => { | ||
const e1 = hot('---^--a--b-----c--d--e--|'); | ||
const unsub = ' ! '; | ||
const e1subs = '^ ! '; | ||
const expected = '---------- '; | ||
rxTest.run(({ hot, expectObservable, expectSubscriptions }) => { | ||
const e1 = hot('---^--a--b-----c--d--e--|'); | ||
const unsub = ' ---------! '; | ||
const e1subs = ' ^--------! '; | ||
const expected = ' ----------------------'; | ||
|
||
expectObservable(e1.pipe(takeLast(42)), unsub).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(42)), unsub).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should work with throw', () => { | ||
const e1 = cold('#'); | ||
const e1subs = '(^!)'; | ||
const expected = '#'; | ||
rxTest.run(({ cold, expectObservable, expectSubscriptions }) => { | ||
const e1 = cold(' #'); | ||
const e1subs = ' (^!)'; | ||
const expected = '#'; | ||
|
||
expectObservable(e1.pipe(takeLast(42))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
expectObservable(e1.pipe(takeLast(42))).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
|
||
it('should throw if total is less than zero', () => { | ||
expect(() => { range(0, 10).pipe(takeLast(-1)); }) | ||
.to.throw(ArgumentOutOfRangeError); | ||
expect(() => { | ||
range(0, 10).pipe(takeLast(-1)); | ||
}).to.throw(ArgumentOutOfRangeError); | ||
}); | ||
|
||
it('should not break unsubscription chain when unsubscribed explicitly', () => { | ||
const e1 = hot('---^--a--b-----c--d--e--|'); | ||
const unsub = ' ! '; | ||
const e1subs = '^ ! '; | ||
const expected = '---------- '; | ||
|
||
const result = e1.pipe( | ||
mergeMap((x: string) => of(x)), | ||
takeLast(42), | ||
mergeMap((x: string) => of(x)) | ||
); | ||
|
||
expectObservable(result, unsub).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
rxTest.run(({ hot, expectObservable, expectSubscriptions }) => { | ||
const e1 = hot('---^--a--b-----c--d--e--|'); | ||
const unsub = ' ---------! '; | ||
const e1subs = ' ^--------! '; | ||
const expected = ' ----------------------'; | ||
|
||
const result = e1.pipe( | ||
mergeMap((x: string) => of(x)), | ||
takeLast(42), | ||
mergeMap((x: string) => of(x)) | ||
); | ||
|
||
expectObservable(result, unsub).toBe(expected); | ||
expectSubscriptions(e1.subscriptions).toBe(e1subs); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.