Improving code coverage (#225)
* Improving overall coverage * Improve jest config * Remove unused internal builder methods * Prefer non-inline snapshots
This commit is contained in:
parent
e58664e671
commit
9f4ab9d830
|
|
@ -10,6 +10,8 @@ jobs:
|
|||
- run: yarn build
|
||||
- run: yarn lint
|
||||
- run: yarn test:ci
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
|
||||
example_apollo-fullstack:
|
||||
docker:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
const path = require("path");
|
||||
|
||||
/**
|
||||
* @type {jest.InitialOptions}
|
||||
*/
|
||||
|
|
@ -11,12 +13,8 @@ module.exports = {
|
|||
],
|
||||
globals: {
|
||||
"ts-jest": {
|
||||
isolatedModules: !process.env.CI,
|
||||
diagnostics: {
|
||||
// FIXME: sloppy, can we make { core } not output in
|
||||
// typegen when its not needed?
|
||||
ignoreCodes: ["6133"],
|
||||
},
|
||||
tsConfig: path.join(__dirname, "tests/tsconfig.json"),
|
||||
},
|
||||
},
|
||||
collectCoverageFrom: ["src/**/*"],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
"scripts": {
|
||||
"dev": "yarn link-examples && tsc -w",
|
||||
"test": "jest",
|
||||
"test:ci": "yarn add file:. && jest -i",
|
||||
"test:ci": "yarn add file:. && jest -i --coverage",
|
||||
"build": "tsc",
|
||||
"lint": "tslint -p tsconfig.json",
|
||||
"clean": "rm -rf dist",
|
||||
|
|
@ -43,9 +43,11 @@
|
|||
"@types/jest": "^23.3.7",
|
||||
"@types/node": "^10.12.2",
|
||||
"@types/prettier": "^1.15.2",
|
||||
"@types/graphql-iso-date": "^3.3.3",
|
||||
"graphql": "^14.0.2",
|
||||
"husky": "^1.1.2",
|
||||
"jest": "^24",
|
||||
"graphql-iso-date": "^3.6.1",
|
||||
"jest-watch-typeahead": "^0.3.1",
|
||||
"lint-staged": "^7.3.0",
|
||||
"nexus": "file:.",
|
||||
|
|
|
|||
|
|
@ -23,14 +23,12 @@ import {
|
|||
GraphQLSchema,
|
||||
GraphQLString,
|
||||
GraphQLUnionType,
|
||||
isEnumType,
|
||||
isInputObjectType,
|
||||
isInterfaceType,
|
||||
isLeafType,
|
||||
isNamedType,
|
||||
isObjectType,
|
||||
isOutputType,
|
||||
isUnionType,
|
||||
isScalarType,
|
||||
defaultFieldResolver,
|
||||
assertValidName,
|
||||
|
|
@ -127,6 +125,7 @@ import {
|
|||
} from "./definitions/extendInputType";
|
||||
import { DynamicInputMethodDef, DynamicOutputMethodDef } from "./dynamicMethod";
|
||||
import { DynamicOutputPropertyDef } from "./dynamicProperty";
|
||||
import { decorateType } from "./definitions/decorateType";
|
||||
|
||||
export type Maybe<T> = T | null;
|
||||
|
||||
|
|
@ -148,18 +147,28 @@ const SCALARS: Record<string, GraphQLScalarType> = {
|
|||
Boolean: GraphQLBoolean,
|
||||
};
|
||||
|
||||
export const UNKNOWN_TYPE_SCALAR = new GraphQLScalarType({
|
||||
name: "NEXUS__UNKNOWN__TYPE__",
|
||||
parseValue(value) {
|
||||
return value;
|
||||
},
|
||||
parseLiteral(value) {
|
||||
return value;
|
||||
},
|
||||
serialize(value) {
|
||||
return value;
|
||||
},
|
||||
});
|
||||
export const UNKNOWN_TYPE_SCALAR = decorateType(
|
||||
new GraphQLScalarType({
|
||||
name: "NEXUS__UNKNOWN__TYPE",
|
||||
description: `
|
||||
This scalar should never make it into production. It is used as a placeholder for situations
|
||||
where GraphQL Nexus encounters a missing type. We don't want to error immedately, otherwise
|
||||
the TypeScript definitions will not be updated.
|
||||
`,
|
||||
parseValue(value) {
|
||||
throw new Error("Error: NEXUS__UNKNOWN__TYPE is not a valid scalar.");
|
||||
},
|
||||
parseLiteral(value) {
|
||||
throw new Error("Error: NEXUS__UNKNOWN__TYPE is not a valid scalar.");
|
||||
},
|
||||
serialize(value) {
|
||||
throw new Error("Error: NEXUS__UNKNOWN__TYPE is not a valid scalar.");
|
||||
},
|
||||
}),
|
||||
{
|
||||
rootTyping: "never",
|
||||
}
|
||||
);
|
||||
|
||||
export interface BuilderConfig {
|
||||
/**
|
||||
|
|
@ -535,6 +544,7 @@ export class SchemaBuilder {
|
|||
addField: (field) => fields.push(field),
|
||||
addDynamicInputFields: (block, isList) =>
|
||||
this.addDynamicInputFields(block, isList),
|
||||
warn: consoleWarn,
|
||||
});
|
||||
config.definition(definitionBlock);
|
||||
const extensions = this.inputTypeExtensionMap[config.name];
|
||||
|
|
@ -576,6 +586,7 @@ export class SchemaBuilder {
|
|||
},
|
||||
addDynamicOutputMembers: (block, isList) =>
|
||||
this.addDynamicOutputMembers(block, isList),
|
||||
warn: consoleWarn,
|
||||
});
|
||||
config.definition(definitionBlock);
|
||||
const extensions = this.typeExtensionMap[config.name];
|
||||
|
|
@ -645,6 +656,7 @@ export class SchemaBuilder {
|
|||
setResolveType: (fn) => (resolveType = fn),
|
||||
addDynamicOutputMembers: (block, isList) =>
|
||||
this.addDynamicOutputMembers(block, isList),
|
||||
warn: consoleWarn,
|
||||
});
|
||||
config.definition(definitionBlock);
|
||||
const extensions = this.typeExtensionMap[config.name];
|
||||
|
|
@ -974,36 +986,6 @@ export class SchemaBuilder {
|
|||
return type;
|
||||
}
|
||||
|
||||
protected getEnum(name: string): GraphQLEnumType {
|
||||
const type = this.getOrBuildType(name);
|
||||
if (!isEnumType(type)) {
|
||||
throw new Error(
|
||||
`Expected ${name} to be an enumType, saw ${type.constructor.name}`
|
||||
);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
protected getUnion(name: string): GraphQLUnionType {
|
||||
const type = this.getOrBuildType(name);
|
||||
if (!isUnionType(type)) {
|
||||
throw new Error(
|
||||
`Expected ${name} to be a unionType, saw ${type.constructor.name}`
|
||||
);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
protected getInputObjectType(name: string): GraphQLInputObjectType {
|
||||
const type = this.getOrBuildType(name);
|
||||
if (!isInputObjectType(type)) {
|
||||
throw new Error(
|
||||
`Expected ${name} to be a valid input type, saw ${type.constructor.name}`
|
||||
);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
protected getInputType(
|
||||
name:
|
||||
| string
|
||||
|
|
@ -1125,9 +1107,9 @@ export class SchemaBuilder {
|
|||
missingResolveType(name: string, location: "union" | "interface") {
|
||||
console.error(
|
||||
new Error(
|
||||
`Missing resolveType for the ${name} ${location}.` +
|
||||
`Missing resolveType for the ${name} ${location}. ` +
|
||||
`Be sure to add one in the definition block for the type, ` +
|
||||
`or t.resolveType(() => null) if you don't want to implement yet`
|
||||
`or t.resolveType(() => null) if you don't want or need to implement.`
|
||||
)
|
||||
);
|
||||
return () => null;
|
||||
|
|
@ -1139,6 +1121,7 @@ export class SchemaBuilder {
|
|||
addField: (f) => this.maybeTraverseInputType(f),
|
||||
addDynamicInputFields: (block, isList) =>
|
||||
this.addDynamicInputFields(block, isList),
|
||||
warn: () => {},
|
||||
});
|
||||
obj.definition(definitionBlock);
|
||||
return obj;
|
||||
|
|
@ -1223,6 +1206,7 @@ export class SchemaBuilder {
|
|||
addField: (f) => this.maybeTraverseOutputType(f),
|
||||
addDynamicOutputMembers: (block, isList) =>
|
||||
this.addDynamicOutputMembers(block, isList),
|
||||
warn: () => {},
|
||||
});
|
||||
obj.definition(definitionBlock);
|
||||
return obj;
|
||||
|
|
@ -1235,6 +1219,7 @@ export class SchemaBuilder {
|
|||
addField: (f) => this.maybeTraverseOutputType(f),
|
||||
addDynamicOutputMembers: (block, isList) =>
|
||||
this.addDynamicOutputMembers(block, isList),
|
||||
warn: () => {},
|
||||
});
|
||||
obj.definition(definitionBlock);
|
||||
return obj;
|
||||
|
|
@ -1531,3 +1516,7 @@ function assertNoMissingTypes(
|
|||
throw new Error("\n" + errors);
|
||||
}
|
||||
}
|
||||
|
||||
function consoleWarn(msg: string) {
|
||||
console.warn(msg);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import { GraphQLNamedType } from "graphql";
|
||||
import { RootTypingDef } from "./_types";
|
||||
import { Maybe } from "../core";
|
||||
|
||||
export interface TypeExtensionConfig {
|
||||
asNexusMethod: string;
|
||||
asNexusMethod?: string;
|
||||
rootTyping?: RootTypingDef;
|
||||
}
|
||||
|
||||
|
|
@ -11,7 +12,7 @@ export type NexusTypeExtensions = {
|
|||
};
|
||||
|
||||
export function decorateType<T extends GraphQLNamedType>(
|
||||
type: T & { extensions?: NexusTypeExtensions },
|
||||
type: T & { extensions?: Maybe<Readonly<Record<string, any>>> },
|
||||
config: TypeExtensionConfig
|
||||
): T & { extensions: NexusTypeExtensions } {
|
||||
type.extensions = {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ export interface OutputDefinitionBuilder {
|
|||
block: OutputDefinitionBlock<any>,
|
||||
isList: boolean
|
||||
): void;
|
||||
warn(msg: string): void;
|
||||
}
|
||||
|
||||
export interface InputDefinitionBuilder {
|
||||
|
|
@ -141,6 +142,7 @@ export interface InputDefinitionBuilder {
|
|||
block: InputDefinitionBlock<any>,
|
||||
isList: boolean
|
||||
): void;
|
||||
warn(msg: string): void;
|
||||
}
|
||||
|
||||
export interface OutputDefinitionBlock<TypeName extends string>
|
||||
|
|
@ -242,7 +244,7 @@ export class OutputDefinitionBlock<TypeName extends string> {
|
|||
protected decorateField(config: NexusOutputFieldDef): NexusOutputFieldDef {
|
||||
if (this.isList) {
|
||||
if (config.list) {
|
||||
console.warn(
|
||||
this.typeBuilder.warn(
|
||||
`It looks like you chained .list and set list for ${config.name}. ` +
|
||||
"You should only do one or the other"
|
||||
);
|
||||
|
|
@ -348,7 +350,7 @@ export class InputDefinitionBlock<TypeName extends string> {
|
|||
protected decorateField(config: NexusInputFieldDef): NexusInputFieldDef {
|
||||
if (this.isList) {
|
||||
if (config.list) {
|
||||
console.warn(
|
||||
this.typeBuilder.warn(
|
||||
`It looks like you chained .list and set list for ${config.name}` +
|
||||
"You should only do one or the other"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import { intArg } from "../definitions/args";
|
|||
|
||||
const basicCollectionMap = new Map<string, NexusObjectTypeDef<string>>();
|
||||
|
||||
export const Collection = dynamicOutputMethod({
|
||||
name: "collection",
|
||||
export const CollectionFieldMethod = dynamicOutputMethod({
|
||||
name: "collectionField",
|
||||
typeDefinition: `<FieldName extends string>(fieldName: FieldName, opts: {
|
||||
type: NexusGenObjectNames | NexusGenInterfaceNames | core.NexusObjectTypeDef<string> | core.NexusInterfaceTypeDef<string>,
|
||||
nodes: core.SubFieldResolver<TypeName, FieldName, "nodes">,
|
||||
|
|
@ -16,27 +16,32 @@ export const Collection = dynamicOutputMethod({
|
|||
description?: string
|
||||
}): void;`,
|
||||
factory({ typeDef: t, args: [fieldName, config] }) {
|
||||
const type =
|
||||
if (!config.type) {
|
||||
throw new Error(
|
||||
`Missing required property "type" from collectionField ${fieldName}`
|
||||
);
|
||||
}
|
||||
const typeName =
|
||||
typeof config.type === "string" ? config.type : config.type.name;
|
||||
if (config.list) {
|
||||
throw new Error(
|
||||
`Collection field ${fieldName}.${type} cannot be used as a list.`
|
||||
`Collection field ${fieldName}.${typeName} cannot be used as a list.`
|
||||
);
|
||||
}
|
||||
if (!basicCollectionMap.has(type)) {
|
||||
if (!basicCollectionMap.has(typeName)) {
|
||||
basicCollectionMap.set(
|
||||
type,
|
||||
typeName,
|
||||
objectType({
|
||||
name: `${type}Collection`,
|
||||
name: `${typeName}Collection`,
|
||||
definition(c) {
|
||||
c.int("totalCount");
|
||||
c.list.field("nodes", { type });
|
||||
c.list.field("nodes", { type: config.type });
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
t.field(fieldName, {
|
||||
type: basicCollectionMap.get(type)!,
|
||||
type: basicCollectionMap.get(typeName)!,
|
||||
args: config.args || {
|
||||
page: intArg(),
|
||||
perPage: intArg(),
|
||||
|
|
|
|||
|
|
@ -7,18 +7,25 @@ const relayConnectionMap = new Map<string, NexusObjectTypeDef<string>>();
|
|||
|
||||
let pageInfo: NexusObjectTypeDef<string>;
|
||||
|
||||
export const RelayConnection = dynamicOutputMethod({
|
||||
name: "relayConnection",
|
||||
typeDefinition: `<FieldName extends string>(fieldName: FieldName, opts: {
|
||||
export const RelayConnectionFieldMethod = dynamicOutputMethod({
|
||||
name: "relayConnectionField",
|
||||
typeDefinition: `
|
||||
<FieldName extends string>(fieldName: FieldName, opts: {
|
||||
type: NexusGenObjectNames | NexusGenInterfaceNames | core.NexusObjectTypeDef<string> | core.NexusInterfaceTypeDef<string>,
|
||||
edges: core.SubFieldResolver<TypeName, FieldName, "edges">,
|
||||
pageInfo: core.SubFieldResolver<TypeName, FieldName, "pageInfo">,
|
||||
args?: Record<string, core.NexusArgDef<string>>,
|
||||
nullable?: boolean,
|
||||
description?: string
|
||||
}): void`,
|
||||
}): void
|
||||
`,
|
||||
factory({ typeDef: t, args: [fieldName, config] }) {
|
||||
const type =
|
||||
if (!config.type) {
|
||||
throw new Error(
|
||||
`Missing required property "type" from relayConnection field ${fieldName}`
|
||||
);
|
||||
}
|
||||
const typeName =
|
||||
typeof config.type === "string" ? config.type : config.type.name;
|
||||
pageInfo =
|
||||
pageInfo ||
|
||||
|
|
@ -31,18 +38,18 @@ export const RelayConnection = dynamicOutputMethod({
|
|||
});
|
||||
if (config.list) {
|
||||
throw new Error(
|
||||
`Collection field ${fieldName}.${type} cannot be used as a list.`
|
||||
`Collection field ${fieldName}.${typeName} cannot be used as a list.`
|
||||
);
|
||||
}
|
||||
if (!relayConnectionMap.has(config.type)) {
|
||||
if (!relayConnectionMap.has(typeName)) {
|
||||
relayConnectionMap.set(
|
||||
config.type,
|
||||
typeName,
|
||||
objectType({
|
||||
name: `${config.type}RelayConnection`,
|
||||
name: `${typeName}RelayConnection`,
|
||||
definition(c) {
|
||||
c.list.field("edges", {
|
||||
type: objectType({
|
||||
name: `${config.type}Edge`,
|
||||
name: `${typeName}Edge`,
|
||||
definition(e) {
|
||||
e.id("cursor");
|
||||
e.field("node", { type: config.type });
|
||||
|
|
@ -55,7 +62,7 @@ export const RelayConnection = dynamicOutputMethod({
|
|||
);
|
||||
}
|
||||
t.field(fieldName, {
|
||||
type: relayConnectionMap.get(config.type)!,
|
||||
type: relayConnectionMap.get(typeName)!,
|
||||
args: {
|
||||
first: intArg(),
|
||||
after: stringArg(),
|
||||
|
|
|
|||
|
|
@ -23,10 +23,6 @@ export function log(msg: string) {
|
|||
console.log(`GraphQL Nexus: ${msg}`);
|
||||
}
|
||||
|
||||
export function withDeprecationComment(description?: string | null) {
|
||||
return description;
|
||||
}
|
||||
|
||||
export const isInterfaceField = (
|
||||
type: GraphQLObjectType,
|
||||
fieldName: string
|
||||
|
|
@ -217,6 +213,7 @@ export function firstDefined<T>(...args: Array<T | undefined>): T {
|
|||
return arg;
|
||||
}
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
throw new Error("At least one of the values should be defined");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`interfaceType can be implemented by object types 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"user": Object {
|
||||
"id": "User:1",
|
||||
"name": "Test User",
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`interfaceType throws if the arg is not provided to the type 1`] = `"You must provide a \\"type\\" for the arg()"`;
|
||||
|
|
@ -1,5 +1,17 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`backingTypes can match backing types for const enums 1`] = `
|
||||
"export interface NexusGenEnums {
|
||||
B: t.B
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`backingTypes can match backing types to regular enums 1`] = `
|
||||
"export interface NexusGenEnums {
|
||||
A: t.A
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`rootTypings can import enum via rootTyping 1`] = `
|
||||
"/**
|
||||
* This file was automatically generated by GraphQL Nexus
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`extendInputType should allow extending input objects 1`] = `
|
||||
"input InputTest {
|
||||
hello: String
|
||||
world: String
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`extendType should allow adding types to the Query type 1`] = `
|
||||
Array [
|
||||
"user",
|
||||
"post",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`inputObjectType should output lists properly, #33 1`] = `
|
||||
"input AddToBasketInput {
|
||||
extras: [ExtraBasketInput!]
|
||||
}"
|
||||
`;
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`dynamicOutputMethod CollectionFieldMethod example 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"cats": Object {
|
||||
"nodes": Array [
|
||||
Object {
|
||||
"id": "Cat:1",
|
||||
"name": "Felix",
|
||||
},
|
||||
Object {
|
||||
"id": "Cat:2",
|
||||
"name": "Booker",
|
||||
},
|
||||
],
|
||||
"totalCount": 2,
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`dynamicOutputMethod RelayConnectionFieldMethod example 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"cats": Object {
|
||||
"edges": Array [
|
||||
Object {
|
||||
"node": Object {
|
||||
"id": "Cat:1",
|
||||
"name": "Felix",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"node": Object {
|
||||
"id": "Cat:2",
|
||||
"name": "Booker",
|
||||
},
|
||||
},
|
||||
],
|
||||
"pageInfo": Object {
|
||||
"hasNextPage": false,
|
||||
"hasPreviousPage": false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`inputObject builds creates an inputObject type 1`] = `
|
||||
Object {
|
||||
"errors": Array [
|
||||
[GraphQLError: Unknown argument "input" on field "user" of type "Query".],
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`inputObject has asArg for using one-off inputObjects inline 1`] = `
|
||||
Object {
|
||||
"errors": Array [
|
||||
[GraphQLError: Unknown argument "input" on field "user" of type "Query".],
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`inputObject throws when chaining .list twice 1`] = `"Cannot chain list.list, in the definition block. Use \`list: []\` config value"`;
|
||||
|
||||
exports[`inputObject warns when specifying .list and list: true 1`] = `
|
||||
Array [
|
||||
"It looks like you chained .list and set list for someFieldYou should only do one or the other",
|
||||
]
|
||||
`;
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`interfaceType can be implemented by object types 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"user": Object {
|
||||
"id": "User:1",
|
||||
"name": "Test User",
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`interfaceType logs error when resolveType is not provided for an interface 1`] = `
|
||||
Array [
|
||||
[Error: Missing resolveType for the Node union. Be sure to add one in the definition block for the type, or t.resolveType(() => null) if you don't want or need to implement.],
|
||||
]
|
||||
`;
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`nonNullDefaults false/false on schema 1`] = `
|
||||
"type Query {
|
||||
test(test: Int): Boolean
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`nonNullDefaults false/false on type 1`] = `
|
||||
"type Query {
|
||||
test(test: Int): Boolean
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`nonNullDefaults true/true on schema 1`] = `
|
||||
"type Query {
|
||||
test(test: Int!): Boolean!
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`nonNullDefaults true/true on type 1`] = `
|
||||
"type Query {
|
||||
test(test: Int!): Boolean!
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`objectType builds creates an object type 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"user": Object {
|
||||
"floatField": 123.4,
|
||||
"id": "User:1",
|
||||
"name": "Test User",
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`objectType throws when chaining .list twice 1`] = `"Cannot chain list.list, in the definition block. Use \`list: []\` config value"`;
|
||||
|
||||
exports[`objectType warns when specifying .list and list: true 1`] = `
|
||||
Array [
|
||||
"It looks like you chained .list and set list for someField. You should only do one or the other",
|
||||
]
|
||||
`;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`scalarType asNexusMethod: should wrap a scalar and make it available on the builder 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"user": Object {
|
||||
"dateTimeField": "2020-01-01T00:00:00.000Z",
|
||||
"id": "User:1",
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SDLConverter printEnumTypes 1`] = `
|
||||
"export const OrderEnum = enumType({
|
||||
name: \\"OrderEnum\\",
|
||||
members: [\\"ASC\\",\\"DESC\\"],
|
||||
});
|
||||
export const SomeEnum = enumType({
|
||||
name: \\"SomeEnum\\",
|
||||
members: [\\"A\\",{\\"name\\":\\"B\\",\\"deprecation\\":\\"This is a deprecation reason for B\\",\\"value\\":\\"B\\"}],
|
||||
});"
|
||||
`;
|
||||
|
||||
exports[`SDLConverter printObjectTypes 1`] = `
|
||||
"export const Mutation = objectType({
|
||||
name: \\"Mutation\\",
|
||||
definition(t) {
|
||||
t.string(\\"someList\\", {
|
||||
list: [false],
|
||||
args: {
|
||||
items: stringArg({
|
||||
list: [false],
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"createPost\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
input: arg({
|
||||
type: CreatePostInput,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"registerClick\\", {
|
||||
type: Query,
|
||||
args: {
|
||||
uuid: uuidArg(),
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
export const Post = objectType({
|
||||
name: \\"Post\\",
|
||||
description: \\"This is a description of a Post\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.uuid(\\"uuid\\")
|
||||
t.field(\\"author\\", { type: User })
|
||||
t.float(\\"geo\\", { list: [true, true] })
|
||||
t.float(\\"messyGeo\\", {
|
||||
list: [true, false],
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
export const Query = objectType({
|
||||
name: \\"Query\\",
|
||||
definition(t) {
|
||||
t.field(\\"user\\", { type: User })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({
|
||||
type: PostFilters,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"unionField\\", { type: ExampleUnion })
|
||||
}
|
||||
})
|
||||
export const User = objectType({
|
||||
name: \\"User\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.string(\\"name\\", {
|
||||
description: \\"This is a description of a name\\",
|
||||
args: {
|
||||
prefix: stringArg({ description: \\"And a description of an arg\\" }),
|
||||
},
|
||||
})
|
||||
t.string(\\"email\\")
|
||||
t.string(\\"phone\\", { nullable: true })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({ type: PostFilters }),
|
||||
},
|
||||
})
|
||||
t.field(\\"outEnum\\", {
|
||||
type: SomeEnum,
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`convertSDL 1`] = `
|
||||
"import { objectType, stringArg, arg, uuidArg, interfaceType, inputObjectType, unionType, enumType, scalarType } from 'nexus';
|
||||
|
||||
export const Mutation = objectType({
|
||||
name: \\"Mutation\\",
|
||||
definition(t) {
|
||||
t.string(\\"someList\\", {
|
||||
list: [false],
|
||||
args: {
|
||||
items: stringArg({
|
||||
list: [false],
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"createPost\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
input: arg({
|
||||
type: CreatePostInput,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"registerClick\\", {
|
||||
type: Query,
|
||||
args: {
|
||||
uuid: uuidArg(),
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
export const Post = objectType({
|
||||
name: \\"Post\\",
|
||||
description: \\"This is a description of a Post\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.uuid(\\"uuid\\")
|
||||
t.field(\\"author\\", { type: User })
|
||||
t.float(\\"geo\\", { list: [true, true] })
|
||||
t.float(\\"messyGeo\\", {
|
||||
list: [true, false],
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
export const Query = objectType({
|
||||
name: \\"Query\\",
|
||||
definition(t) {
|
||||
t.field(\\"user\\", { type: User })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({
|
||||
type: PostFilters,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"unionField\\", { type: ExampleUnion })
|
||||
}
|
||||
})
|
||||
export const User = objectType({
|
||||
name: \\"User\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.string(\\"name\\", {
|
||||
description: \\"This is a description of a name\\",
|
||||
args: {
|
||||
prefix: stringArg({ description: \\"And a description of an arg\\" }),
|
||||
},
|
||||
})
|
||||
t.string(\\"email\\")
|
||||
t.string(\\"phone\\", { nullable: true })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({ type: PostFilters }),
|
||||
},
|
||||
})
|
||||
t.field(\\"outEnum\\", {
|
||||
type: SomeEnum,
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export const Node = interfaceType({
|
||||
name: \\"Node\\",
|
||||
description: \\"This is a description of a Node\\",
|
||||
definition(t) {
|
||||
t.id(\\"id\\")
|
||||
t.resolveType(() => null)
|
||||
}
|
||||
});
|
||||
|
||||
export const CreatePostInput = inputObjectType({
|
||||
name: \\"CreatePostInput\\",
|
||||
definition(t) {
|
||||
t.string(\\"name\\", { required: true })
|
||||
t.id(\\"author\\", { required: true })
|
||||
t.float(\\"geo\\", {
|
||||
list: [false, true],
|
||||
required: true,
|
||||
})
|
||||
}
|
||||
});
|
||||
export const PostFilters = inputObjectType({
|
||||
name: \\"PostFilters\\",
|
||||
definition(t) {
|
||||
t.field(\\"order\\", {
|
||||
type: OrderEnum,
|
||||
required: true,
|
||||
})
|
||||
t.string(\\"search\\")
|
||||
}
|
||||
});
|
||||
|
||||
export const ExampleUnion = unionType({
|
||||
name: \\"ExampleUnion\\",
|
||||
definition(t) {
|
||||
t.members(Post, User)
|
||||
}
|
||||
});
|
||||
|
||||
export const OrderEnum = enumType({
|
||||
name: \\"OrderEnum\\",
|
||||
members: [\\"ASC\\",\\"DESC\\"],
|
||||
});
|
||||
export const SomeEnum = enumType({
|
||||
name: \\"SomeEnum\\",
|
||||
members: [\\"A\\",{\\"name\\":\\"B\\",\\"deprecation\\":\\"This is a deprecation reason for B\\",\\"value\\":\\"B\\"}],
|
||||
});
|
||||
|
||||
export const UUID = scalarType({
|
||||
name: \\"UUID\\",
|
||||
asNexusMethod: \\"uuid\\",
|
||||
serialize() { /* Todo */ },
|
||||
parseValue() { /* Todo */ },
|
||||
parseLiteral() { /* Todo */ }
|
||||
});"
|
||||
`;
|
||||
|
||||
exports[`convertSDL as commonjs 1`] = `
|
||||
"const { objectType, stringArg, arg, uuidArg, interfaceType, inputObjectType, unionType, enumType, scalarType } = require('nexus');
|
||||
|
||||
const Mutation = objectType({
|
||||
name: \\"Mutation\\",
|
||||
definition(t) {
|
||||
t.string(\\"someList\\", {
|
||||
list: [false],
|
||||
args: {
|
||||
items: stringArg({
|
||||
list: [false],
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"createPost\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
input: arg({
|
||||
type: CreatePostInput,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"registerClick\\", {
|
||||
type: Query,
|
||||
args: {
|
||||
uuid: uuidArg(),
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
const Post = objectType({
|
||||
name: \\"Post\\",
|
||||
description: \\"This is a description of a Post\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.uuid(\\"uuid\\")
|
||||
t.field(\\"author\\", { type: User })
|
||||
t.float(\\"geo\\", { list: [true, true] })
|
||||
t.float(\\"messyGeo\\", {
|
||||
list: [true, false],
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
const Query = objectType({
|
||||
name: \\"Query\\",
|
||||
definition(t) {
|
||||
t.field(\\"user\\", { type: User })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({
|
||||
type: PostFilters,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"unionField\\", { type: ExampleUnion })
|
||||
}
|
||||
})
|
||||
const User = objectType({
|
||||
name: \\"User\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.string(\\"name\\", {
|
||||
description: \\"This is a description of a name\\",
|
||||
args: {
|
||||
prefix: stringArg({ description: \\"And a description of an arg\\" }),
|
||||
},
|
||||
})
|
||||
t.string(\\"email\\")
|
||||
t.string(\\"phone\\", { nullable: true })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({ type: PostFilters }),
|
||||
},
|
||||
})
|
||||
t.field(\\"outEnum\\", {
|
||||
type: SomeEnum,
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const Node = interfaceType({
|
||||
name: \\"Node\\",
|
||||
description: \\"This is a description of a Node\\",
|
||||
definition(t) {
|
||||
t.id(\\"id\\")
|
||||
t.resolveType(() => null)
|
||||
}
|
||||
});
|
||||
|
||||
const CreatePostInput = inputObjectType({
|
||||
name: \\"CreatePostInput\\",
|
||||
definition(t) {
|
||||
t.string(\\"name\\", { required: true })
|
||||
t.id(\\"author\\", { required: true })
|
||||
t.float(\\"geo\\", {
|
||||
list: [false, true],
|
||||
required: true,
|
||||
})
|
||||
}
|
||||
});
|
||||
const PostFilters = inputObjectType({
|
||||
name: \\"PostFilters\\",
|
||||
definition(t) {
|
||||
t.field(\\"order\\", {
|
||||
type: OrderEnum,
|
||||
required: true,
|
||||
})
|
||||
t.string(\\"search\\")
|
||||
}
|
||||
});
|
||||
|
||||
const ExampleUnion = unionType({
|
||||
name: \\"ExampleUnion\\",
|
||||
definition(t) {
|
||||
t.members(Post, User)
|
||||
}
|
||||
});
|
||||
|
||||
const OrderEnum = enumType({
|
||||
name: \\"OrderEnum\\",
|
||||
members: [\\"ASC\\",\\"DESC\\"],
|
||||
});
|
||||
const SomeEnum = enumType({
|
||||
name: \\"SomeEnum\\",
|
||||
members: [\\"A\\",{\\"name\\":\\"B\\",\\"deprecation\\":\\"This is a deprecation reason for B\\",\\"value\\":\\"B\\"}],
|
||||
});
|
||||
|
||||
const UUID = scalarType({
|
||||
name: \\"UUID\\",
|
||||
asNexusMethod: \\"uuid\\",
|
||||
serialize() { /* Todo */ },
|
||||
parseValue() { /* Todo */ },
|
||||
parseLiteral() { /* Todo */ }
|
||||
});
|
||||
|
||||
exports.Mutation = Mutation;
|
||||
exports.Post = Post;
|
||||
exports.Query = Query;
|
||||
exports.User = User;
|
||||
exports.Node = Node;
|
||||
exports.CreatePostInput = CreatePostInput;
|
||||
exports.PostFilters = PostFilters;
|
||||
exports.ExampleUnion = ExampleUnion;
|
||||
exports.OrderEnum = OrderEnum;
|
||||
exports.SomeEnum = SomeEnum;
|
||||
exports.UUID = UUID;"
|
||||
`;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`unionType unionType 1`] = `
|
||||
Object {
|
||||
"data": Object {
|
||||
"deletedUserTest": Object {
|
||||
"__typename": "DeletedUser",
|
||||
"message": "This user 1 was deleted",
|
||||
},
|
||||
"userTest": Object {
|
||||
"__typename": "User",
|
||||
"id": 1,
|
||||
"name": "Test User",
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`unknownType should render the typegen but throw 1`] = `
|
||||
[Error:
|
||||
- Missing type User, did you forget to import a type to the root query?]
|
||||
`;
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export const CatListFixture = [
|
||||
{ id: "Cat:1", name: "Felix" },
|
||||
{ id: "Cat:2", name: "Booker" },
|
||||
];
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
import { graphql } from "graphql";
|
||||
import {
|
||||
makeSchema,
|
||||
queryField,
|
||||
booleanArg,
|
||||
floatArg,
|
||||
idArg,
|
||||
stringArg,
|
||||
intArg,
|
||||
objectType,
|
||||
arg,
|
||||
} from "../src/core";
|
||||
|
||||
describe("interfaceType", () => {
|
||||
let schema: ReturnType<typeof makeSchema>;
|
||||
beforeAll(() => {
|
||||
schema = makeSchema({
|
||||
types: [
|
||||
queryField("user", {
|
||||
type: "User",
|
||||
args: {
|
||||
int: intArg(),
|
||||
bool: booleanArg(),
|
||||
float: floatArg(),
|
||||
id: idArg(),
|
||||
str: stringArg(),
|
||||
},
|
||||
resolve: () => ({ id: `User:1`, name: "Test User" }),
|
||||
}),
|
||||
objectType({
|
||||
name: "User",
|
||||
definition(t) {
|
||||
t.id("id");
|
||||
t.string("name");
|
||||
},
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
});
|
||||
it("can be implemented by object types", async () => {
|
||||
expect(
|
||||
await graphql(
|
||||
schema,
|
||||
`
|
||||
{
|
||||
user(int: 1, bool: true, float: 123.45, str: "Test") {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
it("throws if the arg is not provided to the type", async () => {
|
||||
// @ts-ignore
|
||||
expect(() => arg({ type: null })).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -73,11 +73,7 @@ describe("backingTypes", () => {
|
|||
(schema as any).extensions.nexus
|
||||
);
|
||||
|
||||
expect(typegen.printEnumTypeMap()).toMatchInlineSnapshot(`
|
||||
"export interface NexusGenEnums {
|
||||
A: t.A
|
||||
}"
|
||||
`);
|
||||
expect(typegen.printEnumTypeMap()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("can match backing types for const enums", async () => {
|
||||
|
|
@ -89,11 +85,7 @@ describe("backingTypes", () => {
|
|||
(schema as any).extensions.nexus
|
||||
);
|
||||
|
||||
expect(typegen.printEnumTypeMap()).toMatchInlineSnapshot(`
|
||||
"export interface NexusGenEnums {
|
||||
B: t.B
|
||||
}"
|
||||
`);
|
||||
expect(typegen.printEnumTypeMap()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -189,12 +189,7 @@ describe("extendType", () => {
|
|||
UserObject,
|
||||
]).typeMap.Query.getFields()
|
||||
)
|
||||
).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"user",
|
||||
"post",
|
||||
]
|
||||
`);
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -214,12 +209,7 @@ describe("inputObjectType", () => {
|
|||
},
|
||||
}),
|
||||
]);
|
||||
expect(printType(buildTypesMap.typeMap.AddToBasketInput))
|
||||
.toMatchInlineSnapshot(`
|
||||
"input AddToBasketInput {
|
||||
extras: [ExtraBasketInput!]
|
||||
}"
|
||||
`);
|
||||
expect(printType(buildTypesMap.typeMap.AddToBasketInput)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -239,11 +229,6 @@ describe("extendInputType", () => {
|
|||
},
|
||||
}),
|
||||
]);
|
||||
expect(printType(buildTypesMap.typeMap.InputTest)).toMatchInlineSnapshot(`
|
||||
"input InputTest {
|
||||
hello: String
|
||||
world: String
|
||||
}"
|
||||
`);
|
||||
expect(printType(buildTypesMap.typeMap.InputTest)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,230 @@
|
|||
import path from "path";
|
||||
import { GraphQLDateTime } from "graphql-iso-date";
|
||||
import {
|
||||
makeSchema,
|
||||
objectType,
|
||||
queryType,
|
||||
inputObjectType,
|
||||
dynamicInputMethod,
|
||||
decorateType,
|
||||
} from "../src/core";
|
||||
import {
|
||||
RelayConnectionFieldMethod,
|
||||
CollectionFieldMethod,
|
||||
} from "../src/extensions";
|
||||
import { graphql } from "graphql";
|
||||
import { CatListFixture } from "./_fixtures";
|
||||
import { dynamicOutputProperty } from "../src/dynamicProperty";
|
||||
|
||||
let spy: jest.SpyInstance;
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe("dynamicOutputMethod", () => {
|
||||
const Cat = objectType({
|
||||
name: "Cat",
|
||||
definition(t) {
|
||||
t.id("id");
|
||||
t.string("name");
|
||||
},
|
||||
});
|
||||
|
||||
test("RelayConnectionFieldMethod example", async () => {
|
||||
const Query = queryType({
|
||||
definition(t) {
|
||||
// @ts-ignore
|
||||
t.relayConnectionField("cats", {
|
||||
type: Cat,
|
||||
pageInfo: () => ({
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
}),
|
||||
edges: () =>
|
||||
CatListFixture.map((c) => ({ cursor: `Cursor: ${c.id}`, node: c })),
|
||||
});
|
||||
},
|
||||
});
|
||||
const schema = makeSchema({
|
||||
types: [Query, RelayConnectionFieldMethod],
|
||||
outputs: {
|
||||
typegen: path.join(__dirname, "test-output.ts"),
|
||||
schema: path.join(__dirname, "schema.graphql"),
|
||||
},
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
expect(
|
||||
await graphql(
|
||||
schema,
|
||||
`
|
||||
{
|
||||
cats {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("CollectionFieldMethod example", async () => {
|
||||
const dynamicOutputMethod = queryType({
|
||||
definition(t) {
|
||||
// @ts-ignore
|
||||
t.collectionField("cats", {
|
||||
type: Cat,
|
||||
totalCount: () => CatListFixture.length,
|
||||
nodes: () => CatListFixture,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const schema = makeSchema({
|
||||
types: [dynamicOutputMethod, CollectionFieldMethod],
|
||||
outputs: {
|
||||
typegen: path.join(__dirname, "test-output"),
|
||||
schema: path.join(__dirname, "schema.graphql"),
|
||||
},
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
|
||||
expect(
|
||||
await graphql(
|
||||
schema,
|
||||
`
|
||||
{
|
||||
cats {
|
||||
totalCount
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("CollectionFieldMethod example with string type ref", () => {
|
||||
makeSchema({
|
||||
types: [
|
||||
queryType({
|
||||
definition(t) {
|
||||
// @ts-ignore
|
||||
t.collectionField("cats", {
|
||||
type: "Cat",
|
||||
totalCount: () => CatListFixture.length,
|
||||
nodes: () => CatListFixture,
|
||||
});
|
||||
},
|
||||
}),
|
||||
CollectionFieldMethod,
|
||||
],
|
||||
outputs: false,
|
||||
});
|
||||
});
|
||||
|
||||
test("RelayConnectionFieldMethod example with string type ref", async () => {
|
||||
makeSchema({
|
||||
types: [
|
||||
queryType({
|
||||
definition(t) {
|
||||
// @ts-ignore
|
||||
t.relayConnectionField("cats", {
|
||||
type: "Cat",
|
||||
pageInfo: () => ({
|
||||
hasNextPage: false,
|
||||
hasPreviousPage: false,
|
||||
}),
|
||||
edges: () =>
|
||||
CatListFixture.map((c) => ({
|
||||
cursor: `Cursor: ${c.id}`,
|
||||
node: c,
|
||||
})),
|
||||
});
|
||||
},
|
||||
}),
|
||||
RelayConnectionFieldMethod,
|
||||
],
|
||||
outputs: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("dynamicInputMethod", () => {
|
||||
it("should provide a method on the input definition", async () => {
|
||||
makeSchema({
|
||||
types: [
|
||||
decorateType(GraphQLDateTime, {
|
||||
rootTyping: "Date",
|
||||
}),
|
||||
inputObjectType({
|
||||
name: "SomeInput",
|
||||
definition(t) {
|
||||
t.id("id");
|
||||
// @ts-ignore
|
||||
t.timestamps();
|
||||
},
|
||||
}),
|
||||
dynamicInputMethod({
|
||||
name: "timestamps",
|
||||
factory({ typeDef }) {
|
||||
typeDef.field("createdAt", { type: "DateTime" });
|
||||
typeDef.field("updatedAt", { type: "DateTime" });
|
||||
},
|
||||
}),
|
||||
],
|
||||
outputs: {
|
||||
typegen: path.join(__dirname, "test-output.ts"),
|
||||
schema: path.join(__dirname, "schema.graphql"),
|
||||
},
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("dynamicOutputProperty", () => {
|
||||
it("should provide a way for adding a chainable api on the output definition", async () => {
|
||||
makeSchema({
|
||||
types: [
|
||||
decorateType(GraphQLDateTime, {
|
||||
rootTyping: "Date",
|
||||
}),
|
||||
objectType({
|
||||
name: "DynamicPropObject",
|
||||
definition(t) {
|
||||
t.id("id");
|
||||
// @ts-ignore
|
||||
t.model.timestamps();
|
||||
},
|
||||
}),
|
||||
dynamicOutputProperty({
|
||||
name: "model",
|
||||
factory({ typeDef }) {
|
||||
return {
|
||||
timestamps() {
|
||||
typeDef.field("createdAt", { type: "DateTime" });
|
||||
typeDef.field("updatedAt", { type: "DateTime" });
|
||||
},
|
||||
};
|
||||
},
|
||||
}),
|
||||
],
|
||||
outputs: {
|
||||
typegen: path.join(__dirname, "test-output.ts"),
|
||||
schema: path.join(__dirname, "schema.graphql"),
|
||||
},
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
import { graphql } from "graphql";
|
||||
import {
|
||||
makeSchema,
|
||||
objectType,
|
||||
queryField,
|
||||
inputObjectType,
|
||||
} from "../src/core";
|
||||
|
||||
describe("inputObject", () => {
|
||||
it("builds creates an inputObject type", async () => {
|
||||
const schema = makeSchema({
|
||||
types: [
|
||||
inputObjectType({
|
||||
name: "InputObj",
|
||||
definition(t) {
|
||||
t.id("idInput");
|
||||
t.boolean("boolInput");
|
||||
t.float("floatInput");
|
||||
t.int("intInput");
|
||||
},
|
||||
}),
|
||||
objectType({
|
||||
name: "User",
|
||||
definition(t) {
|
||||
t.id("id", {
|
||||
args: {
|
||||
input: "InputObj",
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
queryField("user", {
|
||||
type: "User",
|
||||
resolve: () => ({
|
||||
id: `User:1`,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
expect(
|
||||
await graphql(
|
||||
schema,
|
||||
`
|
||||
{
|
||||
user(input: { boolInput: true, floatInput: 123.4, intInput: 1 }) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("throws when chaining .list twice", () => {
|
||||
expect(() => {
|
||||
makeSchema({
|
||||
types: [
|
||||
inputObjectType({
|
||||
name: "throwingListInput",
|
||||
definition(t) {
|
||||
t.list.list.id("id");
|
||||
},
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
}).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
it("warns when specifying .list and list: true", () => {
|
||||
const spy = jest.spyOn(console, "warn").mockImplementation();
|
||||
makeSchema({
|
||||
types: [
|
||||
inputObjectType({
|
||||
name: "throwingList",
|
||||
definition(t) {
|
||||
t.list.field("someField", {
|
||||
list: true,
|
||||
type: "Boolean",
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
expect(spy.mock.calls[0]).toMatchSnapshot();
|
||||
expect(spy).toBeCalledTimes(1);
|
||||
spy.mockRestore();
|
||||
});
|
||||
|
||||
it("has asArg for using one-off inputObjects inline", async () => {
|
||||
const schema = makeSchema({
|
||||
types: [
|
||||
objectType({
|
||||
name: "User",
|
||||
definition(t) {
|
||||
t.id("id", {
|
||||
args: {
|
||||
input: inputObjectType({
|
||||
name: "InputObj",
|
||||
definition(t) {
|
||||
t.id("idInput");
|
||||
t.boolean("boolInput");
|
||||
t.float("floatInput");
|
||||
t.int("intInput");
|
||||
},
|
||||
}).asArg({ default: { idInput: 1 } }),
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
queryField("user", {
|
||||
type: "User",
|
||||
resolve: () => ({
|
||||
id: `User:1`,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
expect(
|
||||
await graphql(
|
||||
schema,
|
||||
`
|
||||
{
|
||||
user(input: { boolInput: true, floatInput: 123.4, intInput: 1 }) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import { graphql } from "graphql";
|
||||
import path from "path";
|
||||
import { 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");
|
||||
t.resolveType(() => null);
|
||||
},
|
||||
}),
|
||||
objectType({
|
||||
name: "User",
|
||||
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,
|
||||
`
|
||||
{
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
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,
|
||||
});
|
||||
expect(spy.mock.calls[0]).toMatchSnapshot();
|
||||
expect(spy).toBeCalledTimes(1);
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { makeSchema, mutationField } from "../src/core";
|
||||
|
||||
describe("mutationField", () => {
|
||||
it("defines a field on the mutation type as shorthand", () => {
|
||||
makeSchema({
|
||||
types: [
|
||||
mutationField("someField", {
|
||||
type: "String",
|
||||
resolve: () => "Hello World",
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
});
|
||||
});
|
||||
it("can be defined as a thunk", () => {
|
||||
makeSchema({
|
||||
types: [
|
||||
mutationField("someField", () => ({
|
||||
type: "String",
|
||||
resolve: () => "Hello World",
|
||||
})),
|
||||
],
|
||||
outputs: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -11,24 +11,14 @@ describe("nonNullDefaults", () => {
|
|||
output: true,
|
||||
},
|
||||
});
|
||||
expect(printSchema(schema)).toMatchInlineSnapshot(`
|
||||
"type Query {
|
||||
test(test: Int!): Boolean!
|
||||
}
|
||||
"
|
||||
`);
|
||||
expect(printSchema(schema)).toMatchSnapshot();
|
||||
});
|
||||
test("true/true on type", () => {
|
||||
const schema = makeSchema({
|
||||
types: [makeQuery({ nonNullDefaults: { input: true, output: true } })],
|
||||
outputs: false,
|
||||
});
|
||||
expect(printSchema(schema)).toMatchInlineSnapshot(`
|
||||
"type Query {
|
||||
test(test: Int!): Boolean!
|
||||
}
|
||||
"
|
||||
`);
|
||||
expect(printSchema(schema)).toMatchSnapshot();
|
||||
});
|
||||
test("false/false on schema", () => {
|
||||
const schema = makeSchema({
|
||||
|
|
@ -39,24 +29,14 @@ describe("nonNullDefaults", () => {
|
|||
output: false,
|
||||
},
|
||||
});
|
||||
expect(printSchema(schema)).toMatchInlineSnapshot(`
|
||||
"type Query {
|
||||
test(test: Int): Boolean
|
||||
}
|
||||
"
|
||||
`);
|
||||
expect(printSchema(schema)).toMatchSnapshot();
|
||||
});
|
||||
test("false/false on type", () => {
|
||||
const schema = makeSchema({
|
||||
types: [makeQuery({ nonNullDefaults: { input: false, output: false } })],
|
||||
outputs: false,
|
||||
});
|
||||
expect(printSchema(schema)).toMatchInlineSnapshot(`
|
||||
"type Query {
|
||||
test(test: Int): Boolean
|
||||
}
|
||||
"
|
||||
`);
|
||||
expect(printSchema(schema)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
import { graphql } from "graphql";
|
||||
import { makeSchema, objectType, queryField } from "../src/core";
|
||||
|
||||
describe("objectType", () => {
|
||||
it("builds creates an object type", async () => {
|
||||
const schema = makeSchema({
|
||||
types: [
|
||||
objectType({
|
||||
name: "User",
|
||||
definition(t) {
|
||||
t.id("id");
|
||||
t.string("name");
|
||||
t.float("floatField");
|
||||
},
|
||||
}),
|
||||
queryField("user", {
|
||||
type: "User",
|
||||
resolve: () => ({
|
||||
id: `User:1`,
|
||||
name: "Test User",
|
||||
floatField: 123.4,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
expect(
|
||||
await graphql(
|
||||
schema,
|
||||
`
|
||||
{
|
||||
user {
|
||||
id
|
||||
name
|
||||
floatField
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("throws when chaining .list twice", () => {
|
||||
expect(() => {
|
||||
makeSchema({
|
||||
types: [
|
||||
objectType({
|
||||
name: "throwingList",
|
||||
definition(t) {
|
||||
t.list.list.id("id");
|
||||
},
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
}).toThrowErrorMatchingSnapshot();
|
||||
});
|
||||
|
||||
it("warns when specifying .list and list: true", () => {
|
||||
const spy = jest.spyOn(console, "warn").mockImplementation();
|
||||
makeSchema({
|
||||
types: [
|
||||
objectType({
|
||||
name: "throwingList",
|
||||
definition(t) {
|
||||
t.list.field("someField", {
|
||||
list: true,
|
||||
type: "Boolean",
|
||||
});
|
||||
},
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
expect(spy.mock.calls[0]).toMatchSnapshot();
|
||||
expect(spy).toBeCalledTimes(1);
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import { makeSchema, queryField } from "../src/core";
|
||||
|
||||
describe("queryField", () => {
|
||||
it("defines a field on the query type as shorthand", () => {
|
||||
makeSchema({
|
||||
types: [
|
||||
queryField("someField", {
|
||||
type: "String",
|
||||
resolve: () => "Hello World",
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
});
|
||||
});
|
||||
it("can be defined as a thunk", () => {
|
||||
makeSchema({
|
||||
types: [
|
||||
queryField("someField", () => ({
|
||||
type: "String",
|
||||
resolve: () => "Hello World",
|
||||
})),
|
||||
],
|
||||
outputs: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import {
|
||||
makeSchema,
|
||||
objectType,
|
||||
queryField,
|
||||
asNexusMethod,
|
||||
inputObjectType,
|
||||
} from "../src/core";
|
||||
import { graphql } from "graphql";
|
||||
import { GraphQLDateTime, GraphQLDate } from "graphql-iso-date";
|
||||
|
||||
describe("scalarType", () => {
|
||||
it("asNexusMethod: should wrap a scalar and make it available on the builder", async () => {
|
||||
const schema = makeSchema({
|
||||
types: [
|
||||
asNexusMethod(GraphQLDateTime, "dateTime"),
|
||||
asNexusMethod(GraphQLDate, "date"),
|
||||
objectType({
|
||||
name: "User",
|
||||
definition(t) {
|
||||
t.id("id");
|
||||
// @ts-ignore
|
||||
t.dateTime("dateTimeField");
|
||||
},
|
||||
}),
|
||||
queryField("user", {
|
||||
type: "User",
|
||||
args: {
|
||||
input: inputObjectType({
|
||||
name: "SomeInput",
|
||||
definition(t) {
|
||||
// @ts-ignore
|
||||
t.date("date", { required: true });
|
||||
},
|
||||
}).asArg({ required: true }),
|
||||
},
|
||||
resolve: (root, args) => ({
|
||||
id: `User:1`,
|
||||
dateTimeField: args.input.date,
|
||||
}),
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
expect(
|
||||
await graphql(
|
||||
schema,
|
||||
`
|
||||
{
|
||||
user(input: { date: "2020-01-01" }) {
|
||||
id
|
||||
dateTimeField
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -5,434 +5,18 @@ const { SDLConverter } = core;
|
|||
|
||||
describe("SDLConverter", () => {
|
||||
test("printObjectTypes", () => {
|
||||
expect(new SDLConverter(EXAMPLE_SDL).printObjectTypes())
|
||||
.toMatchInlineSnapshot(`
|
||||
"export const Mutation = objectType({
|
||||
name: \\"Mutation\\",
|
||||
definition(t) {
|
||||
t.string(\\"someList\\", {
|
||||
list: [false],
|
||||
args: {
|
||||
items: stringArg({
|
||||
list: [false],
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"createPost\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
input: arg({
|
||||
type: CreatePostInput,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"registerClick\\", {
|
||||
type: Query,
|
||||
args: {
|
||||
uuid: uuidArg(),
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
export const Post = objectType({
|
||||
name: \\"Post\\",
|
||||
description: \\"This is a description of a Post\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.uuid(\\"uuid\\")
|
||||
t.field(\\"author\\", { type: User })
|
||||
t.float(\\"geo\\", { list: [true, true] })
|
||||
t.float(\\"messyGeo\\", {
|
||||
list: [true, false],
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
export const Query = objectType({
|
||||
name: \\"Query\\",
|
||||
definition(t) {
|
||||
t.field(\\"user\\", { type: User })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({
|
||||
type: PostFilters,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"unionField\\", { type: ExampleUnion })
|
||||
}
|
||||
})
|
||||
export const User = objectType({
|
||||
name: \\"User\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.string(\\"name\\", {
|
||||
description: \\"This is a description of a name\\",
|
||||
args: {
|
||||
prefix: stringArg({ description: \\"And a description of an arg\\" }),
|
||||
},
|
||||
})
|
||||
t.string(\\"email\\")
|
||||
t.string(\\"phone\\", { nullable: true })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({ type: PostFilters }),
|
||||
},
|
||||
})
|
||||
t.field(\\"outEnum\\", {
|
||||
type: SomeEnum,
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})"
|
||||
`);
|
||||
expect(new SDLConverter(EXAMPLE_SDL).printObjectTypes()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("printEnumTypes", () => {
|
||||
expect(new SDLConverter(EXAMPLE_SDL).printEnumTypes())
|
||||
.toMatchInlineSnapshot(`
|
||||
"export const OrderEnum = enumType({
|
||||
name: \\"OrderEnum\\",
|
||||
members: [\\"ASC\\",\\"DESC\\"],
|
||||
});
|
||||
export const SomeEnum = enumType({
|
||||
name: \\"SomeEnum\\",
|
||||
members: [\\"A\\",{\\"name\\":\\"B\\",\\"deprecation\\":\\"This is a deprecation reason for B\\",\\"value\\":\\"B\\"}],
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
||||
test("printScalarTypes", () => {
|
||||
expect(new SDLConverter(EXAMPLE_SDL).printScalarTypes())
|
||||
.toMatchInlineSnapshot(`
|
||||
"export const UUID = scalarType({
|
||||
name: \\"UUID\\",
|
||||
asNexusMethod: \\"uuid\\",
|
||||
serialize() { /* Todo */ },
|
||||
parseValue() { /* Todo */ },
|
||||
parseLiteral() { /* Todo */ }
|
||||
});"
|
||||
`);
|
||||
});
|
||||
|
||||
test("printInterfaceTypes", () => {
|
||||
expect(new SDLConverter(EXAMPLE_SDL).printInterfaceTypes())
|
||||
.toMatchInlineSnapshot(`
|
||||
"export const Node = interfaceType({
|
||||
name: \\"Node\\",
|
||||
description: \\"This is a description of a Node\\",
|
||||
definition(t) {
|
||||
t.id(\\"id\\")
|
||||
t.resolveType(() => null)
|
||||
}
|
||||
});"
|
||||
`);
|
||||
expect(new SDLConverter(EXAMPLE_SDL).printEnumTypes()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
test("convertSDL", () => {
|
||||
expect(convertSDL(EXAMPLE_SDL)).toMatchInlineSnapshot(`
|
||||
"import { objectType, stringArg, arg, uuidArg, interfaceType, inputObjectType, unionType, enumType, scalarType } from 'nexus';
|
||||
|
||||
export const Mutation = objectType({
|
||||
name: \\"Mutation\\",
|
||||
definition(t) {
|
||||
t.string(\\"someList\\", {
|
||||
list: [false],
|
||||
args: {
|
||||
items: stringArg({
|
||||
list: [false],
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"createPost\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
input: arg({
|
||||
type: CreatePostInput,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"registerClick\\", {
|
||||
type: Query,
|
||||
args: {
|
||||
uuid: uuidArg(),
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
export const Post = objectType({
|
||||
name: \\"Post\\",
|
||||
description: \\"This is a description of a Post\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.uuid(\\"uuid\\")
|
||||
t.field(\\"author\\", { type: User })
|
||||
t.float(\\"geo\\", { list: [true, true] })
|
||||
t.float(\\"messyGeo\\", {
|
||||
list: [true, false],
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
export const Query = objectType({
|
||||
name: \\"Query\\",
|
||||
definition(t) {
|
||||
t.field(\\"user\\", { type: User })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({
|
||||
type: PostFilters,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"unionField\\", { type: ExampleUnion })
|
||||
}
|
||||
})
|
||||
export const User = objectType({
|
||||
name: \\"User\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.string(\\"name\\", {
|
||||
description: \\"This is a description of a name\\",
|
||||
args: {
|
||||
prefix: stringArg({ description: \\"And a description of an arg\\" }),
|
||||
},
|
||||
})
|
||||
t.string(\\"email\\")
|
||||
t.string(\\"phone\\", { nullable: true })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({ type: PostFilters }),
|
||||
},
|
||||
})
|
||||
t.field(\\"outEnum\\", {
|
||||
type: SomeEnum,
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
export const Node = interfaceType({
|
||||
name: \\"Node\\",
|
||||
description: \\"This is a description of a Node\\",
|
||||
definition(t) {
|
||||
t.id(\\"id\\")
|
||||
t.resolveType(() => null)
|
||||
}
|
||||
});
|
||||
|
||||
export const CreatePostInput = inputObjectType({
|
||||
name: \\"CreatePostInput\\",
|
||||
definition(t) {
|
||||
t.string(\\"name\\", { required: true })
|
||||
t.id(\\"author\\", { required: true })
|
||||
t.float(\\"geo\\", {
|
||||
list: [false, true],
|
||||
required: true,
|
||||
})
|
||||
}
|
||||
});
|
||||
export const PostFilters = inputObjectType({
|
||||
name: \\"PostFilters\\",
|
||||
definition(t) {
|
||||
t.field(\\"order\\", {
|
||||
type: OrderEnum,
|
||||
required: true,
|
||||
})
|
||||
t.string(\\"search\\")
|
||||
}
|
||||
});
|
||||
|
||||
export const ExampleUnion = unionType({
|
||||
name: \\"ExampleUnion\\",
|
||||
definition(t) {
|
||||
t.members(Post, User)
|
||||
}
|
||||
});
|
||||
|
||||
export const OrderEnum = enumType({
|
||||
name: \\"OrderEnum\\",
|
||||
members: [\\"ASC\\",\\"DESC\\"],
|
||||
});
|
||||
export const SomeEnum = enumType({
|
||||
name: \\"SomeEnum\\",
|
||||
members: [\\"A\\",{\\"name\\":\\"B\\",\\"deprecation\\":\\"This is a deprecation reason for B\\",\\"value\\":\\"B\\"}],
|
||||
});
|
||||
|
||||
export const UUID = scalarType({
|
||||
name: \\"UUID\\",
|
||||
asNexusMethod: \\"uuid\\",
|
||||
serialize() { /* Todo */ },
|
||||
parseValue() { /* Todo */ },
|
||||
parseLiteral() { /* Todo */ }
|
||||
});"
|
||||
`);
|
||||
expect(convertSDL(EXAMPLE_SDL)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("convertSDL as commonjs", () => {
|
||||
expect(convertSDL(EXAMPLE_SDL, true)).toMatchInlineSnapshot(`
|
||||
"const { objectType, stringArg, arg, uuidArg, interfaceType, inputObjectType, unionType, enumType, scalarType } = require('nexus');
|
||||
|
||||
const Mutation = objectType({
|
||||
name: \\"Mutation\\",
|
||||
definition(t) {
|
||||
t.string(\\"someList\\", {
|
||||
list: [false],
|
||||
args: {
|
||||
items: stringArg({
|
||||
list: [false],
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"createPost\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
input: arg({
|
||||
type: CreatePostInput,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"registerClick\\", {
|
||||
type: Query,
|
||||
args: {
|
||||
uuid: uuidArg(),
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
const Post = objectType({
|
||||
name: \\"Post\\",
|
||||
description: \\"This is a description of a Post\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.uuid(\\"uuid\\")
|
||||
t.field(\\"author\\", { type: User })
|
||||
t.float(\\"geo\\", { list: [true, true] })
|
||||
t.float(\\"messyGeo\\", {
|
||||
list: [true, false],
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
const Query = objectType({
|
||||
name: \\"Query\\",
|
||||
definition(t) {
|
||||
t.field(\\"user\\", { type: User })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({
|
||||
type: PostFilters,
|
||||
required: true
|
||||
}),
|
||||
},
|
||||
})
|
||||
t.field(\\"unionField\\", { type: ExampleUnion })
|
||||
}
|
||||
})
|
||||
const User = objectType({
|
||||
name: \\"User\\",
|
||||
definition(t) {
|
||||
t.implements(Node)
|
||||
t.string(\\"name\\", {
|
||||
description: \\"This is a description of a name\\",
|
||||
args: {
|
||||
prefix: stringArg({ description: \\"And a description of an arg\\" }),
|
||||
},
|
||||
})
|
||||
t.string(\\"email\\")
|
||||
t.string(\\"phone\\", { nullable: true })
|
||||
t.list.field(\\"posts\\", {
|
||||
type: Post,
|
||||
args: {
|
||||
filters: arg({ type: PostFilters }),
|
||||
},
|
||||
})
|
||||
t.field(\\"outEnum\\", {
|
||||
type: SomeEnum,
|
||||
nullable: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const Node = interfaceType({
|
||||
name: \\"Node\\",
|
||||
description: \\"This is a description of a Node\\",
|
||||
definition(t) {
|
||||
t.id(\\"id\\")
|
||||
t.resolveType(() => null)
|
||||
}
|
||||
});
|
||||
|
||||
const CreatePostInput = inputObjectType({
|
||||
name: \\"CreatePostInput\\",
|
||||
definition(t) {
|
||||
t.string(\\"name\\", { required: true })
|
||||
t.id(\\"author\\", { required: true })
|
||||
t.float(\\"geo\\", {
|
||||
list: [false, true],
|
||||
required: true,
|
||||
})
|
||||
}
|
||||
});
|
||||
const PostFilters = inputObjectType({
|
||||
name: \\"PostFilters\\",
|
||||
definition(t) {
|
||||
t.field(\\"order\\", {
|
||||
type: OrderEnum,
|
||||
required: true,
|
||||
})
|
||||
t.string(\\"search\\")
|
||||
}
|
||||
});
|
||||
|
||||
const ExampleUnion = unionType({
|
||||
name: \\"ExampleUnion\\",
|
||||
definition(t) {
|
||||
t.members(Post, User)
|
||||
}
|
||||
});
|
||||
|
||||
const OrderEnum = enumType({
|
||||
name: \\"OrderEnum\\",
|
||||
members: [\\"ASC\\",\\"DESC\\"],
|
||||
});
|
||||
const SomeEnum = enumType({
|
||||
name: \\"SomeEnum\\",
|
||||
members: [\\"A\\",{\\"name\\":\\"B\\",\\"deprecation\\":\\"This is a deprecation reason for B\\",\\"value\\":\\"B\\"}],
|
||||
});
|
||||
|
||||
const UUID = scalarType({
|
||||
name: \\"UUID\\",
|
||||
asNexusMethod: \\"uuid\\",
|
||||
serialize() { /* Todo */ },
|
||||
parseValue() { /* Todo */ },
|
||||
parseLiteral() { /* Todo */ }
|
||||
});
|
||||
|
||||
exports.Mutation = Mutation;
|
||||
exports.Post = Post;
|
||||
exports.Query = Query;
|
||||
exports.User = User;
|
||||
exports.Node = Node;
|
||||
exports.CreatePostInput = CreatePostInput;
|
||||
exports.PostFilters = PostFilters;
|
||||
exports.ExampleUnion = ExampleUnion;
|
||||
exports.OrderEnum = OrderEnum;
|
||||
exports.SomeEnum = SomeEnum;
|
||||
exports.UUID = UUID;"
|
||||
`);
|
||||
expect(convertSDL(EXAMPLE_SDL, true)).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
import { makeSchema, subscriptionField } from "../src/core";
|
||||
|
||||
describe("subscriptionField", () => {
|
||||
// TODO: Actually validate real subscription usage
|
||||
|
||||
it("defines a field on the mutation type as shorthand", () => {
|
||||
makeSchema({
|
||||
types: [
|
||||
subscriptionField("someField", {
|
||||
type: "String",
|
||||
async subscribe() {
|
||||
let val = 0;
|
||||
return {
|
||||
next() {
|
||||
return `Num:${val++}`;
|
||||
},
|
||||
};
|
||||
},
|
||||
resolve: () => "Hello World",
|
||||
}),
|
||||
],
|
||||
outputs: false,
|
||||
});
|
||||
});
|
||||
it("can be defined as a thunk", () => {
|
||||
makeSchema({
|
||||
types: [
|
||||
subscriptionField("someField", () => ({
|
||||
type: "String",
|
||||
async subscribe() {
|
||||
let val = 0;
|
||||
return {
|
||||
next() {
|
||||
return `Num:${val++}`;
|
||||
},
|
||||
};
|
||||
},
|
||||
resolve: () => "Hello World",
|
||||
})),
|
||||
],
|
||||
outputs: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": ["node", "jest"],
|
||||
"noUnusedLocals": false
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import { graphql } from "graphql";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import {
|
||||
makeSchema,
|
||||
objectType,
|
||||
queryField,
|
||||
unionType,
|
||||
} from "../src/core";
|
||||
|
||||
describe("unionType", () => {
|
||||
test("unionType", async () => {
|
||||
const schema = makeSchema({
|
||||
types: [
|
||||
objectType({
|
||||
name: "DeletedUser",
|
||||
definition(t) {
|
||||
t.string("message", (root) => `This user ${root.id} was deleted`);
|
||||
},
|
||||
rootTyping: `{ id: number; deletedAt: Date }`,
|
||||
}),
|
||||
objectType({
|
||||
name: "User",
|
||||
definition(t) {
|
||||
t.int("id");
|
||||
t.string("name");
|
||||
},
|
||||
rootTyping: `{ id: number; name: string; deletedAt?: null }`,
|
||||
}),
|
||||
unionType({
|
||||
name: "UserOrError",
|
||||
definition(t) {
|
||||
t.members("User", "DeletedUser");
|
||||
t.resolveType((o) => (o.deletedAt ? "DeletedUser" : "User"));
|
||||
},
|
||||
}),
|
||||
queryField("userTest", {
|
||||
type: "UserOrError",
|
||||
resolve: () => ({ id: 1, name: "Test User" }),
|
||||
}),
|
||||
queryField("deletedUserTest", {
|
||||
type: "UserOrError",
|
||||
resolve: () => ({
|
||||
id: 1,
|
||||
name: "Test User",
|
||||
deletedAt: new Date("2019-01-01"),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
outputs: {
|
||||
schema: path.join(__dirname, "unionTypeTest.graphql"),
|
||||
typegen: false,
|
||||
},
|
||||
shouldGenerateArtifacts: false,
|
||||
});
|
||||
expect(
|
||||
await graphql(
|
||||
schema,
|
||||
`
|
||||
fragment UserOrErrorFields on UserOrError {
|
||||
__typename
|
||||
... on User {
|
||||
id
|
||||
name
|
||||
}
|
||||
... on DeletedUser {
|
||||
message
|
||||
}
|
||||
}
|
||||
query UserOrErrorTest {
|
||||
userTest {
|
||||
...UserOrErrorFields
|
||||
}
|
||||
deletedUserTest {
|
||||
...UserOrErrorFields
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { objectType, makeSchemaInternal, makeSchema } from "../src/core";
|
||||
import { objectType, makeSchemaInternal, makeSchema, UNKNOWN_TYPE_SCALAR } from "../src/core";
|
||||
import { Kind } from "graphql";
|
||||
|
||||
describe("unknownType", () => {
|
||||
const Query = objectType({
|
||||
|
|
@ -59,10 +60,23 @@ describe("unknownType", () => {
|
|||
shouldGenerateArtifacts: true,
|
||||
});
|
||||
} catch (e) {
|
||||
expect(e).toMatchInlineSnapshot(`
|
||||
[Error:
|
||||
- Missing type User, did you forget to import a type to the root query?]
|
||||
`);
|
||||
expect(e).toMatchSnapshot()
|
||||
}
|
||||
});
|
||||
|
||||
test("UNKNOWN_TYPE_SCALAR is a scalar, with identity for the implementation", () => {
|
||||
const obj = {};
|
||||
expect(() => {
|
||||
UNKNOWN_TYPE_SCALAR.parseLiteral(
|
||||
{ value: "123.45", kind: Kind.FLOAT },
|
||||
{}
|
||||
);
|
||||
}).toThrowError("Error: NEXUS__UNKNOWN__TYPE is not a valid scalar.");
|
||||
expect(() => UNKNOWN_TYPE_SCALAR.parseValue(obj)).toThrowError(
|
||||
"Error: NEXUS__UNKNOWN__TYPE is not a valid scalar."
|
||||
);
|
||||
expect(() => UNKNOWN_TYPE_SCALAR.serialize(obj)).toThrowError(
|
||||
"Error: NEXUS__UNKNOWN__TYPE is not a valid scalar."
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,12 +9,11 @@
|
|||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"skipLibCheck": true,
|
||||
"declaration": true,
|
||||
"importHelpers": true,
|
||||
"resolveJsonModule": true,
|
||||
"sourceMap": true,
|
||||
"declarationMap": true
|
||||
"types": ["node"]
|
||||
},
|
||||
"exclude": [
|
||||
"./examples",
|
||||
|
|
|
|||
19
yarn.lock
19
yarn.lock
|
|
@ -325,6 +325,13 @@
|
|||
dependencies:
|
||||
"@babel/types" "^7.3.0"
|
||||
|
||||
"@types/graphql-iso-date@^3.3.3":
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/graphql-iso-date/-/graphql-iso-date-3.3.3.tgz#a368aa7370512a9cc87a5035c3701949ed7db9e2"
|
||||
integrity sha512-lchvlAox/yqk2Rcrgqh+uvwc1UC9i1hap+0tqQqyYYcAica6Uw2D4mUkCNcw+WeZ8dvSS5QdtIlJuDYUf4nLXQ==
|
||||
dependencies:
|
||||
graphql "^14.5.3"
|
||||
|
||||
"@types/graphql@14.0.7":
|
||||
version "14.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-14.0.7.tgz#daa09397220a68ce1cbb3f76a315ff3cd92312f6"
|
||||
|
|
@ -1432,6 +1439,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2:
|
|||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.1.tgz#1c1f0c364882c868f5bff6512146328336a11b1d"
|
||||
integrity sha512-b9usnbDGnD928gJB3LrCmxoibr3VE4U2SMo5PBuBnokWyDADTqDPXg4YpwKF1trpH+UbGp7QLicO3+aWEy0+mw==
|
||||
|
||||
graphql-iso-date@^3.6.1:
|
||||
version "3.6.1"
|
||||
resolved "https://registry.yarnpkg.com/graphql-iso-date/-/graphql-iso-date-3.6.1.tgz#bd2d0dc886e0f954cbbbc496bbf1d480b57ffa96"
|
||||
integrity sha512-AwFGIuYMJQXOEAgRlJlFL4H1ncFM8n8XmoVDTNypNOZyQ8LFDG2ppMFlsS862BSTCDcSUfHp8PD3/uJhv7t59Q==
|
||||
|
||||
graphql@^14.0.2:
|
||||
version "14.4.2"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.4.2.tgz#553a7d546d524663eda49ed6df77577be3203ae3"
|
||||
|
|
@ -1439,6 +1451,13 @@ graphql@^14.0.2:
|
|||
dependencies:
|
||||
iterall "^1.2.2"
|
||||
|
||||
graphql@^14.5.3:
|
||||
version "14.5.7"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.5.7.tgz#8646a3fcc07922319cc3967eba4a64b32929f77f"
|
||||
integrity sha512-as410RMJSUFqF8RcH2QWxZ5ioqHzsH9VWnWbaU+UnDXJ/6azMDIYPrtXCBPXd8rlunEVb7W8z6fuUnNHMbFu9A==
|
||||
dependencies:
|
||||
iterall "^1.2.2"
|
||||
|
||||
growly@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
||||
|
|
|
|||
Loading…
Reference in New Issue