Skip to content

Commit

Permalink
Initial compose functionality and basic tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mringer committed May 14, 2018
1 parent 6a9722f commit d9277f6
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 3 deletions.
14 changes: 14 additions & 0 deletions dist/helper.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
export declare const combineResolvers: (resolvers?: any[]) => any;
export declare const and: (...conditions: any[]) => (resolver: any) => any;
export declare const or: (...conditions: any[]) => (resolver: any) => (...query: any[]) => Promise<{}>;
export declare class Composable {
resolver: any;
/**
*
* @param resolver
* TODO: a Resolver type is probably needed, but outside the scope of this PR because it requires refactoring resolver.ts
*/
constructor(resFn: any, errFn: any);
/**
*
* @param resolvers
*/
compose(resolvers: {}): {};
}
26 changes: 26 additions & 0 deletions dist/helper.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/helper.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 35 additions & 1 deletion src/helper.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { createResolver } from "./resolver";
import * as merge from "deepmerge";
import { isFunction } from "./util";

// Helper function to combine multiple resolver definition hashes into a single hash for consumption by Apollostack's graphql-server
export const combineResolvers = (resolvers = []) => resolvers
Expand Down Expand Up @@ -26,4 +28,36 @@ export const or = (...conditions) => resolver => (...query) => {
});
attempt(0);
});
}
}

export class Composable {
resolver: any; // stricter types won't pass the compiler because createResolver is unexpected on Funciton.

/**
*
* @param resolver
* TODO: a Resolver type is probably needed, but outside the scope of this PR because it requires refactoring resolver.ts
*/
constructor(resFn, errFn) {
this.resolver = createResolver(resFn, errFn);
}

/**
*
* @param resolvers
*/
public compose( resolvers: {} ) {
const composed = {};

Object.keys(resolvers).forEach(key => {
const resolver = resolvers[key];

composed[key] = (resolver.resolve || resolver.error)
? this.resolver.createResolver(resolver.resolve, resolver.error)
: this.resolver.createResolver(resolver);
});

return composed;
}

}
99 changes: 98 additions & 1 deletion test/unit/helper_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { expect } from 'chai';
import { stub } from 'sinon';

import {
combineResolvers, and, or,
combineResolvers, and, or, compose, Composable
} from '../../dist/helper';
import { createResolver } from '../../dist/resolver';
import { resolveAll } from 'jspm-config';

describe('(unit) src/helper.js', () => {
describe('combineResolvers', () => {
Expand Down Expand Up @@ -174,4 +175,100 @@ describe('(unit) src/helper.js', () => {
});

});

describe('Compose resolvers', () => {
const compositionErr = new Error('composition error');
const successResolver = createResolver(() => null, () => null);
const failureResolver = createResolver(() => { throw compositionErr; }, () => null);

it('composed resolvers are chained, and base resolver is called for each', () => {

const b = {
resolve: () => {},
error: d => compositionErr
};

stub(b, 'resolve', b.resolve);

const base = new Composable(b.resolve, b.error);
const comp = base.compose({
r1: () => true,
r2: () => true,
r3: () => true,
});

return Promise.all([

comp.r1().then(r => {
expect(b.resolve.calledThrice).to.be.true;
expect(r).to.be.true;
}),

comp.r2().then(r => {
expect(b.resolve.calledThrice).to.be.true;
expect(r).to.be.true;
}),

comp.r3().then(r => {
expect(r).to.be.true;
expect(b.resolve.calledThrice).to.be.true;
})

]);
});

it('when base throws, child is not called ', () => {

const b = {
resolve: null,
error: d => compositionErr
};

const r1 = {
resolve: () => true,
error: () => compositionErr
};

stub(b, 'error', b.error);
stub(r1, 'error', r1.error);

const base = new Composable(b.resolve, b.error);
const comp = base.compose( { r1: r1 } );

comp.r1()
.catch( e => {
expect(b.error.calledOnce).to.be.true;
expect(r1.resolve.notCalled).to.be.true;
expect(r1.error.notCalled).to.be.true;
expect(e).to.equal(compositionErr);
});
});

it('when child throws, parent error is called ', () => {

const b = {
resolve: null,
error: d => null
};

const r1 = {
resolve: () => true,
error: () => compositionErr
};

stub(b, 'error', b.error);
stub(r1, 'error', r1.error);

const base = new Composable(b.resolve, b.error);
const comp = base.compose( { r1: r1 } );

comp.r1()
.catch( e => {
expect(b.error.calledOnce).to.be.true;
expect(r1.error.calledOnce).to.be.true;
expect(e).to.equal(compositionErr);
});
});

});
});

0 comments on commit d9277f6

Please sign in to comment.