Skip to content

Commit 86d07a9

Browse files
committed
Add tests
1 parent c7ab5fa commit 86d07a9

12 files changed

+402
-42
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@
6363
},
6464
"scripts": {
6565
"build": "npm run build-cjs && npm run build-es",
66-
"build-cjs": "rimraf lib && BABEL_ENV=cjs babel src -d lib && cp src/definition.js lib/definition.js",
67-
"build-es": "rimraf es && BABEL_ENV=es babel src -d es && cp src/definition.js es/definition.js",
66+
"build-cjs": "rimraf lib && BABEL_ENV=cjs babel src --ignore __tests__,__mocks__ -d lib && cp src/definition.js lib/definition.js",
67+
"build-es": "rimraf es && BABEL_ENV=es babel src --ignore __tests__,__mocks__ -d es && cp src/definition.js es/definition.js",
6868
"lint": "eslint src test *.js",
6969
"prepublish": "npm run build",
7070
"test": "mocha --compilers js:babel-core/register --reporter dot --require ./resources/mocha-bootload src/**/__tests__/**/*-test.js",
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { TypeComposer } from 'graphql-compose';
2+
import {
3+
GraphQLObjectType,
4+
} from 'graphql';
5+
6+
const RootMutation = new GraphQLObjectType({
7+
name: 'RootMutation',
8+
fields: {
9+
},
10+
});
11+
12+
export const rootMutationTypeComposer = new TypeComposer(RootMutation);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { TypeComposer } from 'graphql-compose';
2+
import {
3+
GraphQLObjectType,
4+
} from 'graphql';
5+
6+
const RootQuery = new GraphQLObjectType({
7+
name: 'RootQuery',
8+
fields: {
9+
},
10+
});
11+
12+
export const rootQueryTypeComposer = new TypeComposer(RootQuery);

src/__mocks__/userTypeComposer.js

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { TypeComposer, Resolver } from 'graphql-compose';
2+
import {
3+
GraphQLString,
4+
GraphQLObjectType,
5+
GraphQLInputObjectType,
6+
GraphQLNonNull,
7+
GraphQLInt,
8+
} from 'graphql';
9+
10+
export const UserType = new GraphQLObjectType({
11+
name: 'User',
12+
fields: {
13+
id: {
14+
type: GraphQLInt,
15+
},
16+
name: {
17+
type: GraphQLString,
18+
},
19+
nickname: {
20+
type: GraphQLString,
21+
},
22+
},
23+
});
24+
25+
export const userTypeComposer = new TypeComposer(UserType);
26+
userTypeComposer.setRecordIdFn(obj => obj.id);
27+
28+
export const findByIdResolver = new Resolver(userTypeComposer, {
29+
name: 'findById',
30+
kind: 'query',
31+
outputType: UserType,
32+
args: {
33+
_id: {
34+
name: '_id',
35+
type: new GraphQLNonNull(GraphQLInt),
36+
},
37+
},
38+
resolve: (resolveParams) => {
39+
const args = resolveParams.args || {};
40+
if (args._id.toString() === '1') {
41+
return Promise.resolve({
42+
id: 1,
43+
name: 'Pavel',
44+
nickname: '@nodkz',
45+
});
46+
}
47+
if (args._id.toString() === '2') {
48+
return Promise.resolve({
49+
id: 2,
50+
name: 'Lee',
51+
nickname: '@leeb',
52+
});
53+
}
54+
return Promise.resolve(null);
55+
},
56+
});
57+
58+
export const createOneResolver = new Resolver(userTypeComposer, {
59+
name: 'createOne',
60+
kind: 'mutation',
61+
outputType: new GraphQLObjectType({
62+
name: 'UserPayload',
63+
fields: {
64+
record: {
65+
type: UserType,
66+
},
67+
},
68+
}),
69+
args: {
70+
input: {
71+
name: 'input',
72+
type: new GraphQLInputObjectType({
73+
name: 'UserInput',
74+
fields: {
75+
name: {
76+
type: GraphQLString,
77+
},
78+
},
79+
}),
80+
},
81+
},
82+
resolve: (resolveParams) => {
83+
return Promise.resolve({
84+
recordId: resolveParams.args.input.id,
85+
record: resolveParams.args && resolveParams.args.input || {},
86+
});
87+
},
88+
});
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { expect } from 'chai';
2+
import { TypeComposer } from 'graphql-compose';
3+
import { composeWithRelay } from '../composeWithRelay';
4+
import { userTypeComposer } from '../__mocks__/userTypeComposer';
5+
import { rootQueryTypeComposer } from '../__mocks__/rootQueryTypeComposer';
6+
import { rootMutationTypeComposer } from '../__mocks__/rootMutationTypeComposer';
7+
import { toGlobalId } from '../globalId';
8+
import {
9+
graphql,
10+
GraphQLInterfaceType,
11+
GraphQLSchema,
12+
GraphQLNonNull,
13+
} from 'graphql';
14+
15+
describe('composeWithRelay', () => {
16+
const userComposer = composeWithRelay(userTypeComposer);
17+
const rootQueryComposer = composeWithRelay(rootQueryTypeComposer);
18+
const rootMutationComposer = composeWithRelay(rootMutationTypeComposer);
19+
20+
describe('basic checks', () => {
21+
it('should return TypeComposer', () => {
22+
expect(userComposer).instanceof(TypeComposer);
23+
});
24+
25+
it('should throw error if get not TypeComposer', () => {
26+
expect(() => composeWithRelay(123)).to.throw('should provide TypeComposer instance');
27+
});
28+
29+
it('should throw error if TypeComposer without recordIdFn', () => {
30+
const tc = userTypeComposer.clone('AnotherUserType2');
31+
delete tc.gqType._gqcGetRecordIdFn;
32+
expect(() => composeWithRelay(tc)).to.throw('should have recordIdFn');
33+
});
34+
35+
it('should thow error if typeComposer does not have findById resolver', () => {
36+
const tc = userTypeComposer.clone('AnotherUserType');
37+
tc.removeResolver('findById');
38+
expect(() => composeWithRelay(tc)).to.throw('should have findById resolver');
39+
});
40+
});
41+
42+
describe('when pass RootQuery type composer', () => {
43+
it('should add `node` field to RootQuery', () => {
44+
const nodeField = rootQueryComposer.getField('node');
45+
expect(nodeField).to.be.ok;
46+
expect(nodeField).property('type').instanceof(GraphQLInterfaceType);
47+
expect(nodeField).deep.property('type.name').to.equal('Node');
48+
});
49+
});
50+
51+
describe('when pass User type composer (not RootQuery)', () => {
52+
it('should add or override id field', () => {
53+
const idField = userComposer.getField('id');
54+
expect(idField.description).to.contain('globally unique ID');
55+
});
56+
57+
it('should make id field NonNull', () => {
58+
const idField = userComposer.getField('id');
59+
expect(idField.type).instanceof(GraphQLNonNull);
60+
});
61+
62+
it('should resolve globalId in `user.id` field', async () => {
63+
rootQueryTypeComposer.addField('user',
64+
userTypeComposer.getResolver('findById').getFieldConfig()
65+
);
66+
const schema = new GraphQLSchema({
67+
query: rootQueryTypeComposer.getType(),
68+
});
69+
const query = `{
70+
user(_id: 1) {
71+
id
72+
name
73+
}
74+
}`;
75+
const result = await graphql(schema, query);
76+
expect(result).deep.property('data.user.id').equal(toGlobalId('User', 1));
77+
expect(result).deep.property('data.user.name').equal('Pavel');
78+
});
79+
80+
it('should resolve globalId in `node.id` field', async () => {
81+
rootQueryTypeComposer.addField('user',
82+
userTypeComposer.getResolver('findById').getFieldConfig()
83+
);
84+
const schema = new GraphQLSchema({
85+
query: rootQueryTypeComposer.getType(),
86+
});
87+
const query = `{
88+
node(id: "${toGlobalId('User', 1)}") {
89+
...user
90+
}
91+
}
92+
fragment user on User {
93+
id
94+
name
95+
}`;
96+
const result = await graphql(schema, query);
97+
expect(result).deep.property('data.node.id').equal(toGlobalId('User', 1));
98+
expect(result).deep.property('data.node.name').equal('Pavel');
99+
});
100+
101+
it('should passthru clientMutationId in mutations', async () => {
102+
rootMutationComposer.addField('createUser',
103+
userTypeComposer.getResolver('createOne').getFieldConfig()
104+
);
105+
const schema = new GraphQLSchema({
106+
query: rootQueryTypeComposer.getType(),
107+
mutation: rootMutationComposer.getType(),
108+
});
109+
const query = `mutation {
110+
createUser(input: { name: "Ok", clientMutationId: "123" }) {
111+
record {
112+
name
113+
}
114+
clientMutationId
115+
}
116+
}`;
117+
const result = await graphql(schema, query);
118+
expect(result).deep.property('data.createUser.record.name')
119+
.equal('Ok');
120+
expect(result).deep.property('data.createUser.clientMutationId')
121+
.equal('123');
122+
});
123+
});
124+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { expect } from 'chai';
2+
import { TypeComposer, InputTypeComposer } from 'graphql-compose';
3+
import { composeWithRelay } from '../composeWithRelay';
4+
import { userTypeComposer } from '../__mocks__/userTypeComposer';
5+
import { toGlobalId } from '../globalId';
6+
import {
7+
GraphQLString,
8+
GraphQLID,
9+
} from 'graphql';
10+
11+
describe('MutationMiddleware', () => {
12+
composeWithRelay(userTypeComposer);
13+
const fieldConfig = userTypeComposer.getResolver('createOne').getFieldConfig();
14+
15+
describe('args', () => {
16+
it('should add `clientMutationId` field to args.input', () => {
17+
const itc = new InputTypeComposer(fieldConfig.args.input.type);
18+
expect(itc.hasField('clientMutationId')).to.be.true;
19+
expect(itc.getFieldType('clientMutationId')).equal(GraphQLString);
20+
});
21+
});
22+
23+
describe('outputType', () => {
24+
it('should add `clientMutationId` field to payload', () => {
25+
const tc = new TypeComposer(fieldConfig.type);
26+
expect(tc.hasField('clientMutationId')).to.be.true;
27+
expect(tc.getFieldType('clientMutationId')).equal(GraphQLString);
28+
});
29+
30+
it('should add `nodeId` field to payload', () => {
31+
const tc = new TypeComposer(fieldConfig.type);
32+
expect(tc.hasField('nodeId')).to.be.true;
33+
expect(tc.getFieldType('nodeId')).equal(GraphQLID);
34+
});
35+
});
36+
37+
describe('resolve', () => {
38+
it('should passthru `clientMutationId`', async () => {
39+
const result = await fieldConfig.resolve(
40+
{},
41+
{ input: { clientMutationId: '333' } },
42+
);
43+
expect(result).property('clientMutationId').equal('333');
44+
});
45+
46+
it('should return `nodeId` with globalId', async () => {
47+
const result = await fieldConfig.resolve(
48+
{},
49+
{ input: { id: 'newRecord' } },
50+
);
51+
expect(result).property('nodeId').equal(toGlobalId('User', 'newRecord'));
52+
});
53+
});
54+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { expect } from 'chai';
2+
import { findByIdResolver } from '../__mocks__/userTypeComposer';
3+
import { toGlobalId } from '../globalId';
4+
import { getNodeFieldConfig } from '../nodeFieldConfig';
5+
import {
6+
GraphQLInterfaceType,
7+
GraphQLNonNull,
8+
} from 'graphql';
9+
10+
describe('nodeFieldConfig', () => {
11+
const typeToFindByIdMap = {
12+
'User': findByIdResolver,
13+
};
14+
const config = getNodeFieldConfig(typeToFindByIdMap);
15+
16+
it('should have type GraphQLInterfaceType', () => {
17+
expect(config).to.be.ok;
18+
expect(config).property('type').instanceof(GraphQLInterfaceType);
19+
expect(config).deep.property('type.name').to.equal('Node');
20+
});
21+
22+
it('should have args with id', () => {
23+
expect(config).deep.property('args.id.type').instanceof(GraphQLNonNull);
24+
});
25+
26+
it('should have resolve function', () => {
27+
expect(config).respondTo('resolve');
28+
});
29+
30+
it('should return null if args.id not defined', () => {
31+
expect(config.resolve({}, {})).to.be.null;
32+
});
33+
34+
it('should return null if findById not defined for type', () => {
35+
expect(
36+
config.resolve({}, { id: toGlobalId('UnexistedType', 1) })
37+
).to.be.null;
38+
});
39+
40+
it('should return Promise if type exists, but id not exist', () => {
41+
expect(
42+
config.resolve({}, { id: toGlobalId('User', 666) })
43+
).instanceof(Promise);
44+
});
45+
46+
it('should return Promise with user data', async () => {
47+
const res = await config.resolve({}, { id: toGlobalId('User', 1) });
48+
expect(res).property('name').to.equal('Pavel');
49+
});
50+
});

0 commit comments

Comments
 (0)