314 lines
7.3 KiB
TypeScript
314 lines
7.3 KiB
TypeScript
import { graphql, GraphQLObjectType } from 'graphql'
|
|
import { DateTimeResolver } from 'graphql-scalars'
|
|
import path from 'path'
|
|
import { asNexusMethod, extendType, interfaceType, makeSchema, objectType, queryField } from '../src/core'
|
|
|
|
describe('interfaceType', () => {
|
|
it('can be implemented by object types', async () => {
|
|
const schema = makeSchema({
|
|
types: [
|
|
interfaceType({
|
|
name: 'Node',
|
|
definition(t) {
|
|
t.id('id')
|
|
// @ts-ignore
|
|
t.dateTime('createdAt')
|
|
},
|
|
resolveType: () => null,
|
|
}),
|
|
asNexusMethod(DateTimeResolver, 'dateTime'),
|
|
objectType({
|
|
name: 'User',
|
|
isTypeOf(data) {
|
|
return typeof data.name === 'string'
|
|
},
|
|
definition(t) {
|
|
t.implements('Node')
|
|
t.string('name')
|
|
},
|
|
}),
|
|
queryField('user', {
|
|
type: 'User',
|
|
resolve: () => ({ id: `User:1`, name: 'Test User' }),
|
|
}),
|
|
],
|
|
outputs: {
|
|
schema: path.join(__dirname, 'interfaceTypeTest.graphql'),
|
|
typegen: false,
|
|
},
|
|
shouldGenerateArtifacts: false,
|
|
})
|
|
expect(
|
|
await graphql({
|
|
schema,
|
|
source: `
|
|
{
|
|
user {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
`,
|
|
})
|
|
).toMatchSnapshot()
|
|
})
|
|
it('can extend other interfaces', async () => {
|
|
const schema = makeSchema({
|
|
types: [
|
|
interfaceType({
|
|
name: 'LivingOrganism',
|
|
definition(t) {
|
|
t.string('type')
|
|
},
|
|
}),
|
|
interfaceType({
|
|
name: 'Animal',
|
|
definition(t) {
|
|
t.implements('LivingOrganism')
|
|
t.string('classification')
|
|
},
|
|
}),
|
|
interfaceType({
|
|
name: 'Pet',
|
|
definition(t) {
|
|
t.implements('Animal')
|
|
t.string('owner')
|
|
},
|
|
}),
|
|
objectType({
|
|
name: 'Dog',
|
|
isTypeOf(data) {
|
|
return typeof data.breed === 'string'
|
|
},
|
|
definition(t) {
|
|
t.implements('Pet')
|
|
t.string('breed')
|
|
},
|
|
}),
|
|
queryField('dog', {
|
|
type: 'Dog',
|
|
resolve: () => ({
|
|
type: 'Animal',
|
|
classification: 'Canis familiaris',
|
|
owner: 'Mark',
|
|
breed: 'Puli',
|
|
}),
|
|
}),
|
|
],
|
|
outputs: {
|
|
schema: path.join(__dirname, 'interfaceTypeTest.graphql'),
|
|
typegen: false,
|
|
},
|
|
shouldGenerateArtifacts: false,
|
|
features: {
|
|
abstractTypeStrategies: {
|
|
resolveType: false,
|
|
isTypeOf: true,
|
|
},
|
|
},
|
|
})
|
|
expect(
|
|
await graphql({
|
|
schema,
|
|
source: `
|
|
{
|
|
dog {
|
|
type
|
|
classification
|
|
owner
|
|
breed
|
|
}
|
|
}
|
|
`,
|
|
})
|
|
).toMatchSnapshot()
|
|
})
|
|
it('can implement interfaces in extend types', async () => {
|
|
const schema = makeSchema({
|
|
types: [
|
|
interfaceType({
|
|
name: 'Node',
|
|
definition(t) {
|
|
t.id('id')
|
|
// @ts-ignore
|
|
t.dateTime('createdAt')
|
|
},
|
|
resolveType: () => null,
|
|
}),
|
|
asNexusMethod(DateTimeResolver, 'dateTime'),
|
|
objectType({
|
|
name: 'User',
|
|
isTypeOf(data) {
|
|
return typeof data.name === 'string'
|
|
},
|
|
definition(t) {
|
|
t.string('name')
|
|
},
|
|
}),
|
|
extendType({
|
|
type: 'User',
|
|
definition(t) {
|
|
t.implements('Node')
|
|
},
|
|
}),
|
|
queryField('user', {
|
|
type: 'User',
|
|
resolve: () => ({ id: `User:1`, name: 'Test User' }),
|
|
}),
|
|
],
|
|
outputs: {
|
|
schema: path.join(__dirname, 'interfaceTypeTest.graphql'),
|
|
typegen: false,
|
|
},
|
|
shouldGenerateArtifacts: false,
|
|
})
|
|
expect(
|
|
await graphql({
|
|
schema,
|
|
source: `
|
|
{
|
|
user {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
`,
|
|
})
|
|
).toMatchSnapshot()
|
|
})
|
|
it('can not implement itself', async () => {
|
|
expect(() =>
|
|
makeSchema({
|
|
types: [
|
|
interfaceType({
|
|
name: 'Node',
|
|
definition(t) {
|
|
t.id('id')
|
|
t.implements('Node')
|
|
},
|
|
}),
|
|
],
|
|
outputs: false,
|
|
shouldGenerateArtifacts: false,
|
|
})
|
|
).toThrowErrorMatchingSnapshot()
|
|
})
|
|
|
|
it('detects circular dependencies', async () => {
|
|
expect(() =>
|
|
makeSchema({
|
|
types: [
|
|
interfaceType({
|
|
name: 'NodeA',
|
|
definition(t) {
|
|
t.id('a')
|
|
t.implements('NodeC')
|
|
},
|
|
}),
|
|
interfaceType({
|
|
name: 'NodeB',
|
|
definition(t) {
|
|
t.id('b')
|
|
t.implements('NodeA')
|
|
},
|
|
}),
|
|
interfaceType({
|
|
name: 'NodeC',
|
|
definition(t) {
|
|
t.id('c')
|
|
t.implements('NodeB')
|
|
},
|
|
}),
|
|
],
|
|
outputs: false,
|
|
shouldGenerateArtifacts: false,
|
|
})
|
|
).toThrowErrorMatchingSnapshot()
|
|
})
|
|
it('logs error when resolveType is not provided for an interface', async () => {
|
|
const spy = jest.spyOn(console, 'error').mockImplementation()
|
|
makeSchema({
|
|
types: [
|
|
interfaceType({
|
|
name: 'Node',
|
|
definition(t) {
|
|
t.id('id')
|
|
},
|
|
}),
|
|
],
|
|
outputs: false,
|
|
shouldGenerateArtifacts: false,
|
|
features: {
|
|
abstractTypeStrategies: {
|
|
resolveType: true,
|
|
},
|
|
},
|
|
})
|
|
expect(spy.mock.calls[0]).toMatchSnapshot()
|
|
expect(spy).toBeCalledTimes(1)
|
|
spy.mockRestore()
|
|
})
|
|
})
|
|
|
|
it('extensions are inherited and deeply merged by field modifications', () => {
|
|
const schema = makeSchema({
|
|
types: [
|
|
interfaceType({
|
|
name: 'SomeInterface',
|
|
resolveType() {
|
|
return 'B'
|
|
},
|
|
definition(t) {
|
|
t.string('foo', {
|
|
extensions: {
|
|
deeply: {
|
|
foo1: true,
|
|
},
|
|
},
|
|
})
|
|
},
|
|
}),
|
|
objectType({
|
|
name: 'A',
|
|
definition(t) {
|
|
t.implements('SomeInterface')
|
|
t.modify('foo', {
|
|
extensions: {
|
|
deeply: {
|
|
foo2: true,
|
|
},
|
|
},
|
|
})
|
|
},
|
|
}),
|
|
],
|
|
outputs: false,
|
|
shouldGenerateArtifacts: false,
|
|
})
|
|
const A = schema.getType('A') as GraphQLObjectType
|
|
expect(A.getFields().foo.extensions).toMatchInlineSnapshot(`
|
|
Object {
|
|
"deeply": Object {
|
|
"foo2": true,
|
|
},
|
|
"nexus": NexusFieldExtension {
|
|
"_type": "NexusFieldExtension",
|
|
"config": Object {
|
|
"configFor": "outputField",
|
|
"extensions": Object {
|
|
"deeply": Object {
|
|
"foo1": true,
|
|
},
|
|
},
|
|
"name": "foo",
|
|
"parentType": "SomeInterface",
|
|
"type": "String",
|
|
"wrapping": undefined,
|
|
},
|
|
"hasDefinedResolver": false,
|
|
"sourceType": undefined,
|
|
},
|
|
}
|
|
`)
|
|
})
|