Files
30-seconds-of-code/node_modules/graphql-compose/mjs/__tests__/Resolver-test.mjs
2019-08-20 15:52:05 +02:00

1058 lines
35 KiB
JavaScript

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
/* eslint-disable no-new, no-unused-vars */
import { graphql, GraphQLString, GraphQLInputObjectType, GraphQLInt, GraphQLFloat, GraphQLNonNull, GraphQLObjectType, GraphQLEnumType, GraphQLList } from '../graphql';
import schemaComposer from '../__mocks__/schemaComposer';
import { Resolver } from '../Resolver';
import { ObjectTypeComposer } from '../ObjectTypeComposer';
import { InputTypeComposer } from '../InputTypeComposer'; // import { Resolver, ObjectTypeComposer, InputTypeComposer, EnumTypeComposer } from '..';
describe('Resolver', () => {
let resolver;
beforeEach(() => {
resolver = new Resolver({
name: 'find'
}, schemaComposer);
});
it('should throw error if not passed name in opts', () => {
expect(() => {
new Resolver({}, schemaComposer);
}).toThrowError();
});
it('should have getDescription/setDescription methods', () => {
resolver.setDescription('Find users');
expect(resolver.getDescription()).toBe('Find users');
});
it('should have getKind/setKind methods', () => {
resolver.setKind('query');
expect(resolver.getKind()).toBe('query');
expect(() => {
resolver.setKind('unproperKind');
}).toThrowError('You provide incorrect value');
});
describe('`type` methods', () => {
it('should have setType/getType methods', () => {
resolver.setType(GraphQLString);
expect(resolver.getType()).toBe(GraphQLString);
expect(() => {
resolver.setType(new GraphQLInputObjectType({
name: 'MyInput',
fields: () => ({})
}));
}).toThrowError();
});
it('should convert type as string to GraphQLType', () => {
const myResolver = new Resolver({
name: 'myResolver',
type: 'String!'
}, schemaComposer);
const type = myResolver.getType();
expect(type).toBeInstanceOf(GraphQLNonNull);
expect(type.ofType).toBe(GraphQLString);
});
it('should convert type definition to GraphQLType', () => {
const myResolver = new Resolver({
name: 'myResolver',
type: `
type SomeType {
name: String
}
`
}, schemaComposer);
const type = myResolver.getType();
expect(type).toBeInstanceOf(GraphQLObjectType);
expect(type.name).toBe('SomeType');
});
it('should accept ObjectTypeComposer for `type` option', () => {
const typeTC = schemaComposer.createObjectTC('type SomeType22 { test: String }');
const myResolver = new Resolver({
name: 'myResolver',
type: typeTC
}, schemaComposer);
const type = myResolver.getType();
expect(type).toBeInstanceOf(GraphQLObjectType);
expect(type.name).toBe('SomeType22');
});
it('should throw error on InputTypeComposer for `type` option', () => {
const someInputITC = schemaComposer.createInputTC('input SomeInputType { add: String }');
expect(() => {
new Resolver({
name: 'myResolver',
type: someInputITC
}, schemaComposer);
}).toThrowError('InputTypeComposer');
});
it('should accept Resolver for `type` option', () => {
const someOtherResolver = new Resolver({
name: 'someOtherResolver',
type: `
type SomeType {
name: String
}
`
}, schemaComposer);
const myResolver = new Resolver({
name: 'myResolver',
type: someOtherResolver
}, schemaComposer);
const type = myResolver.getType();
expect(type).toBeInstanceOf(GraphQLObjectType);
expect(type.name).toBe('SomeType');
});
it('should accept array for `type` option', () => {
const myResolver = new Resolver({
name: 'myResolver',
type: ['String']
}, schemaComposer);
const type = myResolver.getType();
expect(type).toBeInstanceOf(GraphQLList);
expect(type.ofType).toBe(GraphQLString);
});
it('should have wrapType() method', () => {
const newResolver = resolver.wrapType(prevType => {
return 'String';
});
expect(newResolver.getType()).toBe(GraphQLString);
});
});
describe('`args` methods', () => {
it('should have setArg and getArg methods', () => {
resolver.setArg('a1', {
type: GraphQLString
});
expect(resolver.getArgType('a1')).toBe(GraphQLString);
resolver.setArg('a2', {
type: 'String'
});
expect(resolver.getArgType('a2')).toBe(GraphQLString);
resolver.setArg('a3', 'String');
expect(resolver.getArgType('a3')).toBe(GraphQLString);
});
it('should have setArgs method', () => {
resolver.setArgs({
b1: {
type: GraphQLString
},
b2: {
type: 'String'
},
b3: 'String'
});
expect(resolver.getArgType('b1')).toBe(GraphQLString);
expect(resolver.getArgType('b2')).toBe(GraphQLString);
expect(resolver.getArgType('b3')).toBe(GraphQLString);
});
it('should have getArgType method', () => {
resolver.setArgs({
b1: 'String'
});
expect(resolver.getArgType('b1')).toBe(GraphQLString);
expect(() => resolver.getArgType('unexisted')).toThrowError();
});
it('should return undefined for non-existing arg', () => {
expect(resolver.hasArg('unexisted')).toBeFalsy();
});
it('should remove args', () => {
const argName = 'argField';
const argConfig = {
type: GraphQLString
};
resolver.setArg(argName, argConfig);
resolver.removeArg(argName);
expect(resolver.hasArg(argName)).toBeFalsy();
resolver.setArg('a1', 'String');
resolver.setArg('a2', 'String');
resolver.setArg('a3', 'String');
resolver.removeArg(['a1', 'a2']);
expect(resolver.hasArg('a1')).toBeFalsy();
expect(resolver.hasArg('a2')).toBeFalsy();
expect(resolver.hasArg('a3')).toBeTruthy();
});
it('should remove other args', () => {
resolver.setArg('a1', 'String');
resolver.setArg('a2', 'String');
resolver.removeOtherArgs('a1');
expect(resolver.hasArg('a1')).toBeTruthy();
expect(resolver.hasArg('a2')).toBeFalsy();
resolver.setArg('a1', 'String');
resolver.setArg('a2', 'String');
resolver.setArg('a3', 'String');
resolver.removeOtherArgs(['a1', 'a2']);
expect(resolver.hasArg('a1')).toBeTruthy();
expect(resolver.hasArg('a2')).toBeTruthy();
expect(resolver.hasArg('a3')).toBeFalsy();
});
it('should add args', () => {
resolver.setArgs({
b1: 'String'
});
resolver.addArgs({
b2: 'String',
b3: 'String'
});
expect(resolver.hasArg('b1')).toBe(true);
expect(resolver.hasArg('b2')).toBe(true);
expect(resolver.hasArg('b3')).toBe(true);
});
it('should have wrapArgs() method', () => {
const newResolver = resolver.wrapArgs(prevArgs => {
return _objectSpread({}, prevArgs, {
arg1: 'String'
});
});
expect(newResolver.getArgType('arg1')).toBe(GraphQLString);
});
it('should make args required', () => {
resolver.setArgs({
b1: {
type: GraphQLString
},
b2: {
type: 'String'
},
b3: 'String',
b4: 'String'
});
resolver.makeRequired('b1');
resolver.makeRequired(['b2', 'b3']);
expect(resolver.isRequired('b1')).toBe(true);
expect(resolver.getArgType('b1')).toBeInstanceOf(GraphQLNonNull);
expect(resolver.isRequired('b2')).toBe(true);
expect(resolver.isRequired('b3')).toBe(true);
expect(resolver.isRequired('b4')).toBe(false);
});
it('should make args optional', () => {
resolver.setArgs({
b1: {
type: new GraphQLNonNull(GraphQLString)
},
b2: {
type: 'String!'
},
b3: 'String!',
b4: 'String!'
});
resolver.makeOptional('b1');
resolver.makeOptional(['b2', 'b3']);
expect(resolver.isRequired('b1')).toBe(false);
expect(resolver.getArgType('b1')).toBe(GraphQLString);
expect(resolver.isRequired('b2')).toBe(false);
expect(resolver.isRequired('b3')).toBe(false);
expect(resolver.isRequired('b4')).toBe(true);
});
describe('reorderArgs()', () => {
it('should change args order', () => {
resolver.setArgs({
a1: 'Int',
a2: 'Int',
a3: 'Int'
});
expect(resolver.getArgNames().join(',')).toBe('a1,a2,a3');
resolver.reorderArgs(['a3', 'a2', 'a1']);
expect(resolver.getArgNames().join(',')).toBe('a3,a2,a1');
});
it('should append not listed args', () => {
resolver.setArgs({
a1: 'Int',
a2: 'Int',
a3: 'Int'
});
expect(resolver.getArgNames().join(',')).toBe('a1,a2,a3');
resolver.reorderArgs(['a3']);
expect(resolver.getArgNames().join(',')).toBe('a3,a1,a2');
});
it('should skip non existed args', () => {
resolver.setArgs({
a1: 'Int',
a2: 'Int',
a3: 'Int'
});
expect(resolver.getArgNames().join(',')).toBe('a1,a2,a3');
resolver.reorderArgs(['a22', 'a3', 'a55', 'a1', 'a2']);
expect(resolver.getArgNames().join(',')).toBe('a3,a1,a2');
});
});
describe('cloneArg()', () => {
beforeEach(() => {
resolver.setArgs({
scalar: 'String',
filter: {
type: `input FilterInput {
name: String,
age: Int,
}`,
description: 'Data filtering arg'
},
mandatory: {
type: `input Mandatory {
data: String
}`
},
mandatoryScalar: 'String!'
});
resolver.makeRequired('mandatory');
});
it('should throw error if arg does not exists', () => {
expect(() => {
resolver.cloneArg('missingArg', 'NewTypeNameInput');
}).toThrowError('Argument does not exist');
});
it('should throw error if arg is GraphqlNonNull wrapped scalar type', () => {
expect(() => {
resolver.cloneArg('mandatoryScalar', 'NewTypeNameInput');
}).toThrowError('should be GraphQLInputObjectType');
});
it('should throw error if arg is scalar type', () => {
expect(() => {
resolver.cloneArg('scalar', 'NewTypeNameInput');
}).toThrowError('should be GraphQLInputObjectType');
});
it('should throw error if provided incorrect new type name', () => {
expect(() => {
resolver.cloneArg('filter', '');
}).toThrowError('should provide new type name');
expect(() => {
resolver.cloneArg('filter', '#3fdsf');
}).toThrowError('should provide new type name');
expect(() => {
resolver.cloneArg('filter', 'FilterInput');
}).toThrowError('It is equal to current name');
});
it('should clone GraphqlNonNull wrapped types', () => {
resolver.cloneArg('mandatory', 'NewMandatory');
expect(resolver.getArgType('mandatory').ofType.name).toBe('NewMandatory');
});
it('should clone arg type', () => {
resolver.cloneArg('filter', 'NewFilterInput');
expect(resolver.getArgType('filter').name).toBe('NewFilterInput');
expect(resolver.getArgConfig('filter').description).toBe('Data filtering arg');
});
});
it('should work with arg as thunk', () => {
resolver.setArgs({
a: () => 'String',
b: () => schemaComposer.createInputTC(`input ArgAsThunk1 { b: Int }`),
c: () => GraphQLNonNull(schemaComposer.createInputTC(`input ArgAsThunk2 { b: Int }`).getType())
});
expect(resolver.getArgType('a')).toBe(GraphQLString);
expect(resolver.getArgType('b').name).toBe('ArgAsThunk1');
expect(resolver.getArgTC('c')).toBeInstanceOf(InputTypeComposer);
expect(resolver.getArgTC('c').getTypeName()).toBe('ArgAsThunk2');
});
});
describe('getFieldConfig()', () => {
it('should return fieldConfig', () => {
const fc = resolver.getFieldConfig();
expect(fc).toHaveProperty('type');
expect(fc).toHaveProperty('args');
expect(fc).toHaveProperty('description');
expect(fc).toHaveProperty('resolve');
});
it('should combine all resolve args to resolveParams', () => {
let rp;
resolver.resolve = resolveParams => {
rp = resolveParams;
};
const fc = resolver.getFieldConfig();
fc.resolve('sourceData', 'argsData', 'contextData', 'infoData');
expect(rp).toHaveProperty('source', 'sourceData');
expect(rp).toHaveProperty('args', 'argsData');
expect(rp).toHaveProperty('context', 'contextData');
expect(rp).toHaveProperty('info', 'infoData');
});
it('should create `projection` property', () => {
let rp;
resolver.resolve = resolveParams => {
rp = resolveParams;
};
const fc = resolver.getFieldConfig();
fc.resolve();
expect(rp).toHaveProperty('projection');
});
it('should resolve args configs as thunk', () => {
let rp;
resolver.setArgs({
arg1: 'String',
arg2: () => 'String',
arg3: {
type: () => 'String'
}
});
const fc = resolver.getFieldConfig();
expect(fc.args.arg1.type).toBe(GraphQLString);
expect(fc.args.arg2.type).toBe(GraphQLString);
expect(fc.args.arg3.type).toBe(GraphQLString);
});
});
describe('wrap()', () => {
it('should return new resolver', () => {
const newResolver = resolver.wrap();
expect(newResolver).toBeInstanceOf(Resolver);
expect(newResolver).not.toBe(resolver);
});
it('should set internal name', () => {
expect(resolver.wrap().name).toBe('wrap');
expect(resolver.wrap(r => r, {
name: 'crazyWrap'
}).name).toBe('crazyWrap');
});
it('should keep ref to source resolver in parent property', () => {
expect(resolver.wrap().parent).toBe(resolver);
});
it('should return resolver from callback, cause it can be overridden there', () => {
const customResolver = new Resolver({
name: 'find'
}, schemaComposer);
expect(resolver.wrap((newResolver, prevResolver) => {
// eslint-disable-line
return customResolver;
})).toBe(customResolver);
});
});
describe('wrapCloneArg()', () => {
let newResolver;
beforeEach(() => {
resolver.setArgs({
other: '[String]',
filter: {
type: `input FilterInput {
name: String,
age: Int,
}`,
description: 'Data filtering arg'
},
mandatory: {
type: `input Mandatory {
data: String
}`
}
});
resolver.makeRequired('mandatory');
newResolver = resolver.wrapCloneArg('filter', 'NewFilterInput').wrapCloneArg('mandatory', 'NewMandatory');
});
it('should return new resolver', () => {
expect(newResolver).not.toBe(resolver);
});
it('should clone type for argument', () => {
expect(newResolver.getArg('filter')).not.toBe(resolver.getArg('filter'));
expect(newResolver.getArgType('filter')).not.toBe(resolver.getArgType('filter'));
});
it('should change wrapped cloned type names', () => {
const filterType = newResolver.getArgType('filter');
expect(filterType.name).toBe('NewFilterInput');
expect(filterType.name).not.toBe(resolver.getArgType('filter').name);
});
it('should keep untouched other args', () => {
expect(newResolver.getArg('other').type).toBe(resolver.getArg('other').type);
expect(newResolver.getArgType('other')).not.toBe(resolver.getArgType('other'));
});
it('should unwrap GraphQLNonNull types', () => {
expect(newResolver.getArg('mandatory')).not.toBe(resolver.getArg('mandatory'));
expect(newResolver.getArgType('mandatory')).not.toBe(resolver.getArgType('mandatory'));
});
it('should change wrapped cloned type names', () => {
const mandatoryType = newResolver.getArgType('mandatory');
expect(mandatoryType.ofType.name).toBe('NewMandatory');
expect(mandatoryType.ofType.name).not.toBe(resolver.getArgType('mandatory').ofType.name);
});
});
it('should return data from resolve', async () => {
const myResolver = new Resolver({
name: 'customResolver',
resolve: () => ({
name: 'Nodkz'
}),
type: `
type SomeType {
name: String
}
`
}, schemaComposer);
schemaComposer.Query.addRelation('resolveUser', {
resolver: () => myResolver,
projection: {
_id: true
}
});
const schema = schemaComposer.buildSchema();
const result = await graphql(schema, '{ resolveUser { name } }');
expect(result).toEqual({
data: {
resolveUser: {
name: 'Nodkz'
}
}
});
});
describe('addFilterArg', () => {
it('should add arg to filter and setup default value', () => {
const newResolver = resolver.addFilterArg({
name: 'age',
type: 'Int!',
defaultValue: 20,
description: 'Age filter',
filterTypeNameFallback: 'FilterUniqueNameInput'
});
expect(resolver.hasArg('filter')).toBeFalsy();
const filterCfg = newResolver.getArgConfig('filter');
expect(filterCfg).toBeTruthy();
expect(filterCfg.type).toBeInstanceOf(GraphQLInputObjectType);
expect(filterCfg.defaultValue).toEqual({
age: 20
});
const filterITC = schemaComposer.createInputTC(filterCfg.type);
expect(filterITC.getField('age').description).toBe('Age filter');
const ageType = filterITC.getFieldType('age');
expect(ageType).toBeInstanceOf(GraphQLNonNull);
expect(ageType.ofType).toBe(GraphQLInt);
});
it('should prepare resolveParams.rawQuery when `resolve` called', async () => {
let rpSnap;
const resolve = resolver.resolve;
resolver.resolve = rp => {
rpSnap = rp;
return resolve(rp);
};
const newResolver = resolver.addFilterArg({
name: 'age',
type: 'Int!',
description: 'Age filter',
query: (query, value, resolveParams) => {
query.age = {
$gt: value
}; // eslint-disable-line no-param-reassign
query.someKey = resolveParams.someKey; // eslint-disable-line no-param-reassign
},
filterTypeNameFallback: 'FilterUniqueNameInput'
}).addFilterArg({
name: 'isActive',
type: 'Boolean!',
description: 'Active status filter',
query: async (query, value, resolveParams) => {
query.checkPermissions = await Promise.resolve('accessGranted'); // eslint-disable-line no-param-reassign
query.isActive = value; // eslint-disable-line no-param-reassign
},
filterTypeNameFallback: 'FilterOtherUniqueNameInput'
});
await newResolver.resolve({
args: {
filter: {
age: 15,
isActive: false
}
},
someKey: 16
});
expect(rpSnap.rawQuery).toEqual({
age: {
$gt: 15
},
isActive: false,
someKey: 16,
checkPermissions: 'accessGranted'
});
});
it('should extend default value', () => {
resolver.setArg('filter', {
type: new GraphQLInputObjectType({
name: 'MyFilterInput',
fields: {
name: {
type: GraphQLString
}
}
}),
defaultValue: {
name: 'User'
}
});
const newResolver = resolver.addFilterArg({
name: 'age',
type: 'Int',
defaultValue: 33,
filterTypeNameFallback: 'FilterUniqueNameInput'
});
expect(newResolver.getArgConfig('filter').defaultValue).toEqual({
name: 'User',
age: 33
});
});
it('should throw errors if provided incorrect options', () => {
expect(() => {
resolver.addFilterArg({});
}).toThrowError('`opts.name` is required');
expect(() => {
resolver.addFilterArg({
name: 'price'
});
}).toThrowError('`opts.type` is required');
expect(() => {
resolver.addFilterArg({
name: 'price',
type: 'input {min: Int}'
});
}).toThrowError('opts.filterTypeNameFallback: string');
});
});
it('should return nested name for Resolver', () => {
const r1 = new Resolver({
name: 'find'
}, schemaComposer);
const r2 = r1.wrapResolve(next => resolveParams => {
// eslint-disable-line
return 'function code';
});
expect(r1.getNestedName()).toBe('find');
expect(r2.getNestedName()).toBe('wrapResolve(find)');
});
it('should on toString() call provide debug info with source code', () => {
const r1 = new Resolver({
name: 'find'
}, schemaComposer);
const r2 = r1.wrapResolve(next => resolveParams => {
// eslint-disable-line
return 'function code';
});
expect(r2.toString()).toContain('function code');
});
it('should return type by path', () => {
const rsv = new Resolver({
name: 'find',
type: 'type LonLat { lon: Float, lat: Float }',
args: {
distance: 'Int!'
}
}, schemaComposer);
expect(rsv.get('lat').getType()).toBe(GraphQLFloat);
expect(rsv.get('@distance').getType()).toBe(GraphQLInt);
});
describe('addSortArg', () => {
it('should extend SortEnum by new value', () => {
resolver.setArg('sort', {
type: new GraphQLEnumType({
name: 'MySortEnum',
values: {
AGE_ASC: {}
}
})
});
const newResolver = resolver.addSortArg({
name: 'PRICE_ASC',
description: 'Asc sort by non-null price',
value: {
price: 1
}
});
const sortEnum = newResolver.getArgType('sort');
expect(sortEnum.parseValue('AGE_ASC')).toBe('AGE_ASC');
expect(sortEnum.parseValue('PRICE_ASC')).toEqual({
price: 1
});
});
it('should prepare sort value when `resolve` called', () => {
let rpSnap;
const resolve = resolver.resolve;
resolver.resolve = rp => {
rpSnap = rp;
return resolve(rp);
};
let whereSnap;
const query = {
where: condition => {
whereSnap = condition;
}
};
const newResolver = resolver.addSortArg({
name: 'PRICE_ASC',
description: 'Asc sort by non-null price',
value: resolveParams => {
resolveParams.query.where({
price: {
$gt: 0
}
}); // eslint-disable-line no-param-reassign
return {
price: 1
};
},
sortTypeNameFallback: 'SortEnum'
});
newResolver.resolve({
args: {
sort: 'PRICE_ASC'
},
query
});
expect(rpSnap.args.sort).toEqual({
price: 1
});
expect(whereSnap).toEqual({
price: {
$gt: 0
}
});
});
it('should work with arg defined as TypeStringDefinition', () => {
resolver.setArg('sort', `enum CustomEnum { ID_ASC, ID_DESC }`);
resolver.addSortArg({
name: 'PRICE_ASC',
value: 123
});
const sortType = resolver.getArgType('sort');
const etc = schemaComposer.createEnumTC(sortType);
expect(etc.getFieldNames()).toEqual(['ID_ASC', 'ID_DESC', 'PRICE_ASC']);
});
it('should throw errors if provided incorrect options', () => {
expect(() => {
resolver.addSortArg({});
}).toThrowError('`opts.name` is required');
expect(() => {
resolver.addSortArg({
name: 'PRICE_ASC'
});
}).toThrowError('`opts.value` is required');
expect(() => {
resolver.addSortArg({
name: 'PRICE_ASC',
value: 123
});
}).toThrowError('opts.sortTypeNameFallback: string');
expect(() => {
resolver.setArg('sort', {
type: GraphQLInt
});
resolver.addSortArg({
name: 'PRICE_ASC',
value: 123
});
}).toThrowError('must have `sort` arg with type GraphQLEnumType');
});
});
it('should have chainable methods', () => {
expect(resolver.setArgs({})).toBe(resolver);
expect(resolver.setArg('a1', 'String')).toBe(resolver);
expect(resolver.addArgs({
a2: 'input LL { f1: Int, f2: Int }'
})).toBe(resolver);
expect(resolver.removeArg('a1')).toBe(resolver);
expect(resolver.removeOtherArgs('a2')).toBe(resolver);
expect(resolver.reorderArgs(['a1'])).toBe(resolver);
expect(resolver.cloneArg('a2', 'NewTypeName')).toBe(resolver);
expect(resolver.makeRequired('a2')).toBe(resolver);
expect(resolver.makeOptional('a2')).toBe(resolver);
expect(resolver.setResolve(() => {})).toBe(resolver);
expect(resolver.setType('String')).toBe(resolver);
expect(resolver.setKind('query')).toBe(resolver);
expect(resolver.setDescription('Find method')).toBe(resolver);
});
describe('debug methods', () => {
/* eslint-disable no-console */
const origConsole = global.console;
beforeEach(() => {
global.console = {
log: jest.fn(),
dir: jest.fn(),
time: jest.fn(),
timeEnd: jest.fn()
};
});
afterEach(() => {
global.console = origConsole;
});
describe('debugExecTime()', () => {
it('should measure execution time', async () => {
const r1 = new Resolver({
name: 'find',
displayName: 'User.find()',
resolve: () => {}
}, schemaComposer);
await r1.debugExecTime().resolve(undefined);
expect(console.time.mock.calls[0]).toEqual(['Execution time for User.find()']);
expect(console.timeEnd.mock.calls[0]).toEqual(['Execution time for User.find()']);
});
});
describe('debugParams()', () => {
it('should show resolved payload', () => {
const r1 = new Resolver({
name: 'find',
displayName: 'User.find()',
resolve: () => {}
}, schemaComposer);
r1.debugParams().resolve({
source: {
id: 1
},
args: {
limit: 1
},
context: {
isAdmin: true,
db: {}
},
info: {
fieldName: 'a',
otherAstFields: {}
}
});
expect(console.log.mock.calls[0]).toEqual(['ResolveParams for User.find():']);
expect(console.dir.mock.calls[0]).toEqual([{
args: {
limit: 1
},
context: {
db: 'Object {} [[hidden]]',
isAdmin: true
},
info: 'Object {} [[hidden]]',
source: {
id: 1
},
'[debug note]': 'Some data was [[hidden]] to display this fields use debugParams("info context.db")'
}, {
colors: true,
depth: 5
}]);
});
it('should show filtered resolved payload', () => {
const r1 = new Resolver({
name: 'find',
displayName: 'User.find()',
resolve: () => {}
}, schemaComposer);
r1.debugParams('args, args.sort, source.name').resolve({
source: {
id: 1,
name: 'Pavel'
},
args: {
limit: 1,
sort: 'id'
}
});
expect(console.log.mock.calls[0]).toEqual(['ResolveParams for User.find():']);
expect(console.dir.mock.calls[0]).toEqual([{
args: {
limit: 1,
sort: 'id'
},
'args.sort': 'id',
'source.name': 'Pavel'
}, {
colors: true,
depth: 5
}]);
});
});
describe('debugPayload()', () => {
it('should show resolved payload', async () => {
const r1 = new Resolver({
name: 'find',
displayName: 'User.find()',
resolve: async () => ({
a: 123
})
}, schemaComposer);
await r1.debugPayload().resolve(undefined);
expect(console.log.mock.calls[0]).toEqual(['Resolved Payload for User.find():']);
expect(console.dir.mock.calls[0]).toEqual([{
a: 123
}, {
colors: true,
depth: 5
}]);
});
it('should show filtered resolved payload', async () => {
const r1 = new Resolver({
name: 'find',
displayName: 'User.find()',
resolve: async () => ({
a: 123,
b: 345,
c: [0, 1, 2, 3]
})
}, schemaComposer);
await r1.debugPayload(['b', 'c.3']).resolve(undefined);
expect(console.log.mock.calls[0]).toEqual(['Resolved Payload for User.find():']);
expect(console.dir.mock.calls[0]).toEqual([{
b: 345,
'c.3': 3
}, {
colors: true,
depth: 5
}]);
});
it('should show rejected payload', async () => {
const err = new Error('Request failed');
const r1 = new Resolver({
name: 'find',
displayName: 'User.find()',
resolve: async () => {
throw err;
}
}, schemaComposer);
await r1.debugPayload().resolve(undefined).catch(e => {});
expect(console.log.mock.calls[0]).toEqual(['Rejected Payload for User.find():']);
expect(console.log.mock.calls[1]).toEqual([err]);
});
});
describe('debug()', () => {
it('should output execution time, resolve params and payload', async () => {
const r1 = new Resolver({
name: 'find',
displayName: 'User.find()',
resolve: () => ({
a: 123,
b: 345,
c: [0, 1, 2, 3]
})
}, schemaComposer);
await r1.debug({
params: 'args.sort source.name',
payload: 'b, c.3'
}).resolve({
source: {
id: 1,
name: 'Pavel'
},
args: {
limit: 1,
sort: 'id'
}
});
expect(console.time.mock.calls[0]).toEqual(['Execution time for User.find()']);
expect(console.timeEnd.mock.calls[0]).toEqual(['Execution time for User.find()']);
expect(console.log.mock.calls[0]).toEqual(['ResolveParams for debugExecTime(User.find()):']);
expect(console.dir.mock.calls[0]).toEqual([{
'args.sort': 'id',
'source.name': 'Pavel'
}, {
colors: true,
depth: 2
}]);
expect(console.log.mock.calls[1]).toEqual(['Resolved Payload for debugParams(debugExecTime(User.find())):']);
expect(console.dir.mock.calls[1]).toEqual([{
b: 345,
'c.3': 3
}, {
colors: true,
depth: 2
}]);
});
});
/* eslint-enable no-console */
});
describe('getArgTC()', () => {
const myResolver = new Resolver({
name: 'someResolver',
type: 'String',
args: {
scalar: 'String',
list: '[Int]',
obj: schemaComposer.createInputTC(`input RCustomInputType { name: String }`),
objArr: [schemaComposer.createInputTC(`input RCustomInputType2 { name: String }`)]
}
}, schemaComposer);
it('should return InputTypeComposer for object argument', () => {
const objTC = myResolver.getArgTC('obj');
expect(objTC.getTypeName()).toBe('RCustomInputType');
});
it('should return InputTypeComposer for wrapped object argument', () => {
const objTC = myResolver.getArgTC('objArr');
expect(objTC.getTypeName()).toBe('RCustomInputType2');
});
it('should throw error for non-object argument', () => {
expect(() => {
myResolver.getArgTC('scalar');
}).toThrow('argument should be InputObjectType');
expect(() => {
myResolver.getArgTC('list');
}).toThrow('argument should be InputObjectType');
});
});
describe('getTypeComposer()', () => {
it('should return ObjectTypeComposer for GraphQLObjectType', () => {
const r = new Resolver({
name: 'find',
type: `type MyOutputType { name: String }`,
displayName: 'User.find()',
resolve: () => {}
}, schemaComposer);
expect(r.getType()).toBeInstanceOf(GraphQLObjectType);
expect(r.getTypeComposer()).toBeInstanceOf(ObjectTypeComposer);
expect(r.getTypeComposer().getTypeName()).toBe('MyOutputType');
});
it('should unwrap List and NonNull GraphQLObjectType', () => {
schemaComposer.createObjectTC(`type MyOutputType { name: String }`);
const r = new Resolver({
name: 'find',
type: '[MyOutputType!]!',
displayName: 'User.find()',
resolve: () => {}
}, schemaComposer);
expect(r.type).toBe('[MyOutputType!]!');
const type = r.getType();
expect(type).toBeInstanceOf(GraphQLNonNull);
expect(type.ofType).toBeInstanceOf(GraphQLList);
expect(r.getTypeComposer()).toBeInstanceOf(ObjectTypeComposer);
expect(r.getTypeComposer().getTypeName()).toBe('MyOutputType');
});
it('should throw error if output type is not GraphQLObjectType', () => {
const r = new Resolver({
name: 'find',
type: 'String',
displayName: 'User.find()',
resolve: () => {}
}, schemaComposer);
expect(r.type).toBe('String');
expect(r.getType()).toBe(GraphQLString);
expect(() => r.getTypeComposer()).toThrow();
});
});
describe('withMiddlewares()', () => {
let r;
const log = [];
beforeEach(() => {
r = new Resolver({
name: 'find',
type: 'String',
displayName: 'User.find()',
resolve: () => {
log.push('call User.find()');
return 'users result';
}
}, schemaComposer);
});
it('should apply middlewares', async () => {
const mw1 = async (resolve, source, args, context, info) => {
log.push('m1.before');
const res = await resolve(source, args, context, info);
log.push('m1.after');
return res;
};
const mw2 = async (resolve, source, args, context, info) => {
log.push('m2.before');
const res = await resolve(source, args, context, info);
log.push('m2.after');
return res;
};
const res = await r.withMiddlewares([mw1, mw2]).resolve({});
expect(res).toBe('users result');
expect(log).toEqual(['m1.before', 'm2.before', 'call User.find()', 'm2.after', 'm1.after']);
});
});
});