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']); }); }); });