@ch1/rpc
Version:
JavaScript Remote Procedure Call (RPC)
228 lines (190 loc) • 5.39 kB
text/typescript
import * as rpc from './js-rpc';
import { DEFAULT_MESSAGE } from './constants';
import { noop } from './utils';
describe('basic async e2e', () => {
let configA;
let configB;
let callbacksA;
let callbacksB;
beforeEach(() => {
callbacksA = [];
callbacksB = [];
configA = {
emit: (...args) => {
setTimeout(() => {
callbacksB.filter(Boolean).forEach(cb => cb.apply(null, args));
}, 0);
},
enableStackTrace: false,
message: '',
on: listener => {
const offset = callbacksA.push(listener);
return () => {
callbacksA[offset - 1] = null;
};
},
remote: {},
};
configB = {
emit: (...args) => {
setTimeout(() => {
callbacksA.filter(Boolean).forEach(cb => cb.apply(null, args));
}, 0);
},
enableStackTrace: false,
message: '',
on: listener => {
const offset = callbacksB.push(listener);
return () => {
callbacksB[offset - 1] = null;
};
},
remote: {},
};
});
it('should work for two simple functions (promises)', done => {
interface Test1 {
test1(): string;
}
interface Test2 {
test2(): string;
}
const a = rpc.create<Test2>(configA, {
test1: () => new Promise(resolve => resolve('testA')),
});
const b = rpc.create<Test1>(configB, {
test2: () => new Promise(resolve => resolve('testB')),
});
Promise.all([a.ready, b.ready])
.then(() => {
return Promise.all([a.remote.test2(), b.remote.test1()]).then(
results => {
expect(results[0]).toBe('testB');
expect(results[1]).toBe('testA');
},
);
})
.catch(err => {
expect(err.message).toBeUndefined();
})
.then(() => {
a.destroy();
b.destroy();
done();
});
});
it('should work for two simple functions (synchronous invocations)', done => {
interface Test1 {
test1(): string;
}
interface Test2 {
test2(): string;
}
const a = rpc.create<Test2>(configA, {
test1: () => 'test A',
});
const b = rpc.create<Test1>(configB, {
test2: () => 'test B',
});
Promise.all([a.ready, b.ready])
.then(() => {
return Promise.all([a.remote.test2(), b.remote.test1()]).then(
results => {
expect(results[0]).toBe('test B');
expect(results[1]).toBe('test A');
},
);
})
.catch(err => {
expect(err.message).toBeUndefined();
})
.then(() => {
a.destroy();
b.destroy();
done();
});
});
it('has an on destroy notification system', done => {
interface Test2 {
test2(): string;
}
const a = rpc.create<Test2>(configA, {
test1: () => new Promise(resolve => resolve('testA')),
});
a.onDestroy(reason => {
expect(reason).toBe('test fails');
done();
});
a.destroy('test fails').catch(done);
});
it('can deregister on destroy handlers', done => {
interface Test2 {
test2(): string;
}
const a = rpc.create<Test2>(configA, {
test1: () => new Promise(resolve => resolve('testA')),
});
let onDestroyRan = false;
const dereg = a.onDestroy(reason => {
onDestroyRan = true;
});
dereg();
a.destroy('test fails')
.then(() => {
// this seems hackier than it actually is
setTimeout(() => {
expect(onDestroyRan).toBe(false);
done();
}, 10);
})
.catch(done);
});
});
describe('js-rpc functions', () => {
let config;
beforeEach(() => {
config = {
emit: noop,
enableStackTrace: false,
message: '',
on: noop,
remote: {},
};
});
describe('create function', () => {
it('should run without incident if given valid parameters', () => {
expect(() => rpc.create<Object>(config, {})).not.toThrowError();
});
it('should provide a destroy function', () => {
const test = rpc.create<Object>(config, {});
expect(() => test.destroy()).not.toThrowError();
});
});
describe('validateRemote function', () => {
it('should resolve if a given value is falsey', () => {
expect(() => rpc.validateRemote(null)).not.toThrowError();
});
it('should throw if given value is truthy and also not an object', () => {
expect(() => rpc.validateRemote('hello')).toThrowError();
});
it('should not throw if given a valid object', () => {
expect(() => rpc.validateRemote({})).not.toThrowError();
});
});
describe('validateConfig function', () => {
it('should return an immutable (frozen) object', () => {
const validConfig = rpc.validateConfig(config, {});
expect(() => (validConfig.message = 'test')).toThrowError();
});
it('should default to having a false stack prop', () => {
config.enableStackTrace = '';
const validConfig = rpc.validateConfig(config, {});
expect(validConfig.enableStackTrace).toBe(false);
});
it('should default to using DEFAULT_MESSAGE', () => {
config.message = '';
const validConfig = rpc.validateConfig(config, {});
expect(validConfig.message).toBe(DEFAULT_MESSAGE);
});
});
});