Skip to content

Commit

Permalink
Merge pull request #3360 from benlesh/refactor-bound-thingies
Browse files Browse the repository at this point in the history
Refactor bindCallback
  • Loading branch information
benlesh authored Mar 8, 2018
2 parents d231053 + 2535641 commit 35bb058
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 433 deletions.
197 changes: 48 additions & 149 deletions spec/observables/bindCallback-spec.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { expect } from 'chai';
import * as sinon from 'sinon';
import * as Rx from '../../src/internal/Rx';
import { bindCallback } from '../../src/';
import { TestScheduler } from '../../src/testing';

declare const rxTestScheduler: Rx.TestScheduler;
const Observable = Rx.Observable;
declare const rxTestScheduler: TestScheduler;

/** @test {bindCallback} */
describe('Observable.bindCallback', () => {
describe('bindCallback', () => {
describe('when not scheduled', () => {
it('should emit undefined from a callback without arguments', () => {
function callback(cb) {
function callback(cb: Function) {
cb();
}
const boundCallback = Observable.bindCallback(callback);
const results = [];
const boundCallback = bindCallback(callback);
const results: Array<string|number> = [];

boundCallback()
.subscribe((x: any) => {
Expand All @@ -26,14 +26,14 @@ describe('Observable.bindCallback', () => {
});

it('should emit one value from a callback', () => {
function callback(datum, cb) {
function callback(datum: number, cb: Function) {
cb(datum);
}
const boundCallback = Observable.bindCallback(callback);
const results = [];
const boundCallback = bindCallback<number>(callback);
const results: Array<string|number> = [];

boundCallback(42)
.subscribe((x: number) => {
.subscribe(x => {
results.push(x);
}, null, () => {
results.push('done');
Expand All @@ -43,12 +43,12 @@ describe('Observable.bindCallback', () => {
});

it('should set callback function context to context of returned function', () => {
function callback(cb) {
function callback(this: any, cb: Function) {
cb(this.datum);
}

const boundCallback = Observable.bindCallback(callback);
const results = [];
const boundCallback = bindCallback(callback);
const results: Array<string|number> = [];

boundCallback.apply({datum: 5})
.subscribe(
Expand All @@ -60,52 +60,18 @@ describe('Observable.bindCallback', () => {
expect(results).to.deep.equal([5, 'done']);
});

it('should emit one value chosen by a selector', () => {
function callback(datum, cb) {
cb(datum);
}
const boundCallback = Observable.bindCallback(callback, (datum: any) => datum);
const results = [];

boundCallback(42)
.subscribe((x: number) => {
results.push(x);
}, null, () => {
results.push('done');
});

expect(results).to.deep.equal([42, 'done']);
});

it('should emit an error when the selector throws', () => {
const expected = new Error('Yikes!');
function callback(cb) {
cb(42);
}
const boundCallback = Observable.bindCallback(callback, (err: any) => { throw expected; });

boundCallback()
.subscribe(() => {
throw 'should not next';
}, (err: any) => {
expect(err).to.equal(expected);
}, () => {
throw 'should not complete';
});
});

it('should not emit, throw or complete if immediately unsubscribed', (done: MochaDone) => {
const nextSpy = sinon.spy();
const throwSpy = sinon.spy();
const completeSpy = sinon.spy();
let timeout;
function callback(datum, cb) {
let timeout: number;
function callback(datum: number, cb: Function) {
// Need to cb async in order for the unsub to trigger
timeout = setTimeout(() => {
cb(datum);
});
}
const subscription = Observable.bindCallback(callback)(42)
const subscription = bindCallback(callback)(42)
.subscribe(nextSpy, throwSpy, completeSpy);
subscription.unsubscribe();

Expand All @@ -122,14 +88,14 @@ describe('Observable.bindCallback', () => {

describe('when scheduled', () => {
it('should emit undefined from a callback without arguments', () => {
function callback(cb) {
function callback(cb: Function) {
cb();
}
const boundCallback = Observable.bindCallback(callback, null, rxTestScheduler);
const results = [];
const boundCallback = bindCallback(callback, rxTestScheduler);
const results: Array<string|number> = [];

boundCallback()
.subscribe((x: any) => {
.subscribe(x => {
results.push(typeof x);
}, null, () => {
results.push('done');
Expand All @@ -141,14 +107,14 @@ describe('Observable.bindCallback', () => {
});

it('should emit one value from a callback', () => {
function callback(datum, cb) {
function callback(datum: number, cb: Function) {
cb(datum);
}
const boundCallback = Observable.bindCallback(callback, null, rxTestScheduler);
const results = [];
const boundCallback = bindCallback<number>(callback, rxTestScheduler);
const results: Array<string|number> = [];

boundCallback(42)
.subscribe((x: number) => {
.subscribe(x => {
results.push(x);
}, null, () => {
results.push('done');
Expand All @@ -160,14 +126,14 @@ describe('Observable.bindCallback', () => {
});

it('should set callback function context to context of returned function', () => {
function callback(cb) {
function callback(this: { datum: number }, cb: Function) {
cb(this.datum);
}

const boundCallback = Observable.bindCallback(callback, null, rxTestScheduler);
const results = [];
const boundCallback = bindCallback(callback, rxTestScheduler);
const results: Array<string|number> = [];

boundCallback.apply({datum: 5})
boundCallback.apply({ datum: 5 })
.subscribe(
(x: number) => results.push(x),
null,
Expand All @@ -181,77 +147,32 @@ describe('Observable.bindCallback', () => {

it('should error if callback throws', () => {
const expected = new Error('haha no callback for you');
function callback(datum, cb) {
throw expected;
}
const boundCallback = Observable.bindCallback(callback, null, rxTestScheduler);

boundCallback(42)
.subscribe((x: number) => {
throw 'should not next';
}, (err: any) => {
expect(err).to.equal(expected);
}, () => {
throw 'should not complete';
});

rxTestScheduler.flush();
});

it('should error if selector throws', () => {
const expected = new Error('what? a selector? I don\'t think so');
function callback(datum, cb) {
cb(datum);
}
function selector() {
function callback(datum: number, cb: Function): never {
throw expected;
}
const boundCallback = Observable.bindCallback(callback, selector, rxTestScheduler);
const boundCallback = bindCallback(callback, rxTestScheduler);

boundCallback(42)
.subscribe((x: any) => {
throw 'should not next';
.subscribe(x => {
throw new Error('should not next');
}, (err: any) => {
expect(err).to.equal(expected);
}, () => {
throw 'should not complete';
});

rxTestScheduler.flush();
});

it('should use a selector', () => {
function callback(datum, cb) {
cb(datum);
}
function selector(x) {
return x + '!!!';
}
const boundCallback = Observable.bindCallback(callback, selector, rxTestScheduler);
const results = [];

boundCallback(42)
.subscribe((x: string) => {
results.push(x);
}, null, () => {
results.push('done');
throw new Error('should not complete');
});

rxTestScheduler.flush();

expect(results).to.deep.equal(['42!!!', 'done']);
});
});

it('should pass multiple inner arguments as an array', () => {
function callback(datum, cb) {
function callback(datum: number, cb: Function) {
cb(datum, 1, 2, 3);
}
const boundCallback = Observable.bindCallback(callback, null, rxTestScheduler);
const results = [];
const boundCallback = bindCallback<number[]>(callback, rxTestScheduler);
const results: Array<string|number[]> = [];

boundCallback(42)
.subscribe((x: number) => {
.subscribe(x => {
results.push(x);
}, null, () => {
results.push('done');
Expand All @@ -262,48 +183,25 @@ describe('Observable.bindCallback', () => {
expect(results).to.deep.equal([[42, 1, 2, 3], 'done']);
});

it('should pass multiple inner arguments to the selector if there is one', () => {
function callback(datum, cb) {
cb(datum, 1, 2, 3);
}
function selector(a, b, c, d) {
expect([a, b, c, d]).to.deep.equal([42, 1, 2, 3]);
return a + b + c + d;
}
const boundCallback = Observable.bindCallback(callback, selector, rxTestScheduler);
const results = [];

boundCallback(42)
.subscribe((x: number) => {
results.push(x);
}, null, () => {
results.push('done');
});

rxTestScheduler.flush();

expect(results).to.deep.equal([48, 'done']);
});

it('should cache value for next subscription and not call callbackFunc again', () => {
let calls = 0;
function callback(datum, cb) {
function callback(datum: number, cb: Function) {
calls++;
cb(datum);
}
const boundCallback = Observable.bindCallback(callback, null, rxTestScheduler);
const results1 = [];
const results2 = [];
const boundCallback = bindCallback<number>(callback, rxTestScheduler);
const results1: Array<number|string> = [];
const results2: Array<number|string> = [];

const source = boundCallback(42);

source.subscribe((x: number) => {
source.subscribe(x => {
results1.push(x);
}, null, () => {
results1.push('done');
});

source.subscribe((x: number) => {
source.subscribe(x => {
results2.push(x);
}, null, () => {
results2.push('done');
Expand All @@ -318,12 +216,12 @@ describe('Observable.bindCallback', () => {

it('should not even call the callbackFn if immediately unsubscribed', () => {
let calls = 0;
function callback(datum, cb) {
function callback(datum: number, cb: Function) {
calls++;
cb(datum);
}
const boundCallback = Observable.bindCallback(callback, null, rxTestScheduler);
const results1 = [];
const boundCallback = bindCallback(callback, rxTestScheduler);
const results1: Array<number|string> = [];

const source = boundCallback(42);

Expand All @@ -339,4 +237,5 @@ describe('Observable.bindCallback', () => {

expect(calls).to.equal(0);
});
});
});
Loading

0 comments on commit 35bb058

Please sign in to comment.