voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
252 lines (227 loc) • 6.05 kB
text/typescript
/**
* @license
* Copyright 2017 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { assert } from 'chai';
import * as sinon from 'sinon';
import { async, createSubscribe, Observer, Subscribe } from '../src/subscribe';
describe('createSubscribe', () => {
let spy: any;
beforeEach(() => {
// Listen to console.error calls.
spy = sinon.spy(console, 'error');
});
afterEach(() => {
spy.restore();
});
it('Creation', done => {
const subscribe = createSubscribe<number>((observer: Observer<number>) => {
observer.next(123);
});
const unsub = subscribe((value: number) => {
unsub();
assert.equal(value, 123);
done();
});
});
it('Logging observer error to console', done => {
const uncatchableError = new Error('uncatchable');
const subscribe = createSubscribe<number>((observer: Observer<number>) => {
observer.next(123);
observer.complete();
});
subscribe({
next(value) {
assert.equal(value, 123);
// Simulate an error is thrown in the next callback.
// This should log to the console as an error.
throw uncatchableError;
},
complete() {
// By this point, the error should have been logged.
assert.ok(spy.calledWith(uncatchableError));
done();
}
});
});
it('Well-defined subscription order', done => {
const subscribe = createSubscribe<number>(observer => {
observer.next(123);
// Subscription after value emitted should NOT be received.
subscribe({
next(_value) {
assert.ok(false);
}
});
});
// Subscription before value emitted should be recieved.
subscribe({
next(_value) {
done();
}
});
});
it('Subscribing to already complete Subscribe', done => {
let seq = 0;
const subscribe = createSubscribe<number>(observer => {
observer.next(456);
observer.complete();
});
subscribe({
next(value: number) {
assert.equal(seq++, 0);
assert.equal(value, 456);
},
complete() {
subscribe({
complete() {
assert.equal(seq++, 1);
done();
}
});
}
});
});
it('Subscribing to errored Subscribe', done => {
let seq = 0;
const subscribe = createSubscribe<number>(observer => {
observer.next(246);
observer.error(new Error('failure'));
});
subscribe({
next(value: number) {
assert.equal(seq++, 0);
assert.equal(value, 246);
},
error(e) {
assert.equal(seq++, 1);
subscribe({
error(_e2) {
assert.equal(seq++, 2);
assert.equal(e.message, 'failure');
done();
}
});
},
complete() {
assert.ok(false);
}
});
});
it('Delayed value', done => {
const subscribe = createSubscribe<number>((observer: Observer<number>) => {
setTimeout(() => observer.next(123), 10);
});
subscribe((value: number) => {
assert.equal(value, 123);
done();
});
});
it('Executor throws => Error', () => {
// It's an application error to throw an exception in the executor -
// but since it is called asynchronously, our only option is
// to emit that Error and terminate the Subscribe.
const subscribe = createSubscribe<number>((_observer: Observer<number>) => {
throw new Error('Executor throws');
});
subscribe({
error(e) {
assert.equal(e.message, 'Executor throws');
}
});
});
it('Sequence', done => {
const subscribe = makeCounter(10);
let j = 1;
subscribe({
next(value: number) {
assert.equal(value, j++);
},
complete() {
assert.equal(j, 11);
done();
}
});
});
it('unlisten', done => {
const subscribe = makeCounter(10);
subscribe({
complete: () => {
async(done)();
}
});
let j = 1;
const unsub = subscribe({
next: (value: number) => {
assert.ok(value <= 5);
assert.equal(value, j++);
if (value === 5) {
unsub();
}
},
complete: () => {
assert.ok(false, 'Does not call completed if unsubscribed');
}
});
});
it('onNoObservers', done => {
const subscribe = makeCounter(10);
let j = 1;
const unsub = subscribe({
next: (value: number) => {
assert.ok(value <= 5);
assert.equal(value, j++);
if (value === 5) {
unsub();
async(done)();
}
},
complete: () => {
assert.ok(false, 'Does not call completed if unsubscribed');
}
});
});
// TODO(koss): Add test for partial Observer (missing methods).
it('Partial Observer', done => {
const subscribe = makeCounter(10);
const _unsub = subscribe({
complete: () => {
done();
}
});
});
});
function makeCounter(maxCount: number, ms = 10): Subscribe<number> {
let id: any;
return createSubscribe<number>(
(observer: Observer<number>) => {
let i = 1;
id = setInterval(() => {
observer.next(i++);
if (i > maxCount) {
if (id) {
clearInterval(id);
id = undefined;
}
observer.complete();
}
}, ms);
},
(_observer: Observer<number>) => {
clearInterval(id);
id = undefined;
}
);
}