From 5bc3036c63fc4c658f80e4b6920494caf70cf432 Mon Sep 17 00:00:00 2001 From: Joel Marcotte Date: Tue, 10 Mar 2020 09:27:43 -0400 Subject: [PATCH] feat: basic cases, and test todos * working through thought model * visitor * passing test * more tests for base cases * test cleanup * can mock interfaces * bunch of test todos * cleanup * fix tsc * fix coverage Co-authored-by: Joel Marcotte --- README.md | 52 ++----- jest.config.js | 6 + package.json | 9 +- src/__tests__/lib.test.ts | 277 +++++++++++++++++++++++++++++++++++++- src/global.d.ts | 1 + src/index.ts | 194 +++++++++++++++++++++++++- tsconfig.json | 2 +- yarn.lock | 240 ++++++++++++++++++++++++++++++++- 8 files changed, 729 insertions(+), 52 deletions(-) create mode 100644 src/global.d.ts diff --git a/README.md b/README.md index bd476d7..52b52e3 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,11 @@

graphql-ergonomock

- An awesome README template to jumpstart your projects! + Developer-friendly automock for GraphQL
Explore the docs »

- View Demo - · Report Bug · Request Feature @@ -49,9 +47,8 @@ - [About The Project](#about-the-project) - [Built With](#built-with) - [Getting Started](#getting-started) - - [Prerequisites](#prerequisites) - [Installation](#installation) -- [Usage](#usage) + - [Usage](#usage) - [Roadmap](#roadmap) - [Contributing](#contributing) - [License](#license) @@ -78,50 +75,25 @@ A list of commonly used resources that I find helpful are listed in the acknowle ### Built With This section should list any major frameworks that you built your project using. Leave any add-ons/plugins for the acknowledgements section. Here are a few examples. -* [Bootstrap](https://getbootstrap.com) -* [JQuery](https://jquery.com) -* [Laravel](https://laravel.com) +* [Typescript](https://www.typescriptlang.org/) +* [GraphQL](https://graphql.org) +* [Jest](https://jestjs.io) ## Getting Started -This is an example of how you may give instructions on setting up your project locally. -To get a local copy up and running follow these simple example steps. - -### Prerequisites - -This is an example of how to list things you need to use the software and how to install them. -* npm -```sh -npm install npm@latest -g -``` - ### Installation -1. Get a free API Key at [https://example.com](https://example.com) -2. Clone the repo -```sh -git clone https://github.com/your_username_/Project-Name.git -``` -3. Install NPM packages -```sh -npm install +```shell +npm i graphql-ergonomock --save-dev ``` -4. Enter your API in `config.js` -```JS -const API_KEY = 'ENTER YOUR API'; -``` - - -## Usage +### Usage -Use this space to show useful examples of how a project can be used. Additional screenshots, code examples and demos work well in this space. You may also link to more resources. - -_For more examples, please refer to the [Documentation](https://example.com)_ +TBD @@ -155,14 +127,16 @@ Distributed under the MIT License. See `LICENSE` for more information. ## Contact -Your Name - [@your_twitter](https://twitter.com/your_username) - email@example.com +Your Name - [@your_twitter](https://twitter.com/joual) - email@example.com -Project Link: [https://github.com/your_username/repo_name](https://github.com/your_username/repo_name) +Project Link: [https://github.com/joual/graphql-ergonomock](https://github.com/joual/graphql-ergonomock) ## Acknowledgements + +TBD * [GitHub Emoji Cheat Sheet](https://www.webpagefx.com/tools/emoji-cheat-sheet) * [Img Shields](https://shields.io) * [Choose an Open Source License](https://choosealicense.com) diff --git a/jest.config.js b/jest.config.js index d6c5574..42fd990 100644 --- a/jest.config.js +++ b/jest.config.js @@ -3,5 +3,11 @@ module.exports = { testMatch: ["__tests__/**/*.+(ts|tsx|js)", "**/?(*.)+(spec|test).+(ts|tsx|js)"], transform: { "^.+\\.(ts|tsx)$": "ts-jest" + }, + setupFilesAfterEnv: ["jest-extended"], + globals: { + "ts-jest": { + diagnostics: false + } } }; diff --git a/package.json b/package.json index 1f00d0b..6c62f7f 100644 --- a/package.json +++ b/package.json @@ -13,10 +13,11 @@ "devDependencies": { "@types/jest": "25.1.4", "jest": "25.1.0", + "jest-extended": "^0.11.5", "prettier": "1.19.1", + "semantic-release": "17.0.4", "ts-jest": "25.2.1", - "typescript": "3.8.3", - "semantic-release": "17.0.4" + "typescript": "3.8.3" }, "renovate": { "extends": [ @@ -26,5 +27,9 @@ "repository": { "type": "git", "url": "https://github.com/joual/graphql-ergonomock.git" + }, + "dependencies": { + "graphql": "^14.6.0", + "graphql-tools": "^4.0.7" } } diff --git a/src/__tests__/lib.test.ts b/src/__tests__/lib.test.ts index 7b8cb2f..8471040 100644 --- a/src/__tests__/lib.test.ts +++ b/src/__tests__/lib.test.ts @@ -1,5 +1,276 @@ -import lib from ".."; +import { mock } from ".."; +import { buildSchemaFromTypeDefinitions } from "graphql-tools"; +import { visitWithTypeInfo } from "graphql"; +// import { graphql, GraphQLResolveInfo } from "graphql"; -test("it returns true", () => { - expect(lib()).toBe(true); +const schemaSDL = /* GraphQL */ ` + scalar MissingMockType + interface Flying { + id: String! + returnInt: Int + } + type Bird implements Flying { + id: String! + returnInt: Int + returnString: String + returnStringArgument(s: String): String + } + type Bee implements Flying { + id: String! + returnInt: Int + returnEnum: SomeEnum + } + union BirdsAndBees = Bird | Bee + enum SomeEnum { + A + B + C + } + type RootQuery { + returnInt: Int + returnFloat: Float + returnString: String + returnBoolean: Boolean + returnID: ID + returnEnum: SomeEnum + returnBirdsAndBees: [BirdsAndBees] + returnFlying: [Flying] + returnMockError: MissingMockType + returnNullableString: String + returnNonNullString: String! + returnObject: Bird + returnListOfInt: [Int] + returnListOfIntArg(l: Int): [Int] + returnListOfListOfInt: [[Int!]!]! + returnListOfListOfIntArg(l: Int): [[Int]] + returnListOfListOfObject: [[Bird!]]! + returnStringArgument(s: String): String + node(id: String!): Flying + node2(id: String!): BirdsAndBees + } + type RootMutation { + returnStringArgument(s: String): String + } + schema { + query: RootQuery + mutation: RootMutation + } +`; + +const schema = buildSchemaFromTypeDefinitions(schemaSDL); + +describe("Automocking", () => { + describe("Guardrails", () => { + test.todo("it throws without a schema"); + test.todo("it throws without a valid schema"); + test.todo("it throws without a query"); + test.todo("it throws without a valid query"); + }); + + describe("No provided mocks", () => { + test("automocks the default types automatically", () => { + const testQuery = /* GraphQL */ ` + { + returnInt + returnString + returnFloat + returnBoolean + returnID + } + `; + const resp: any = mock(schema, testQuery); + expect(resp.data).toMatchObject({ + returnInt: expect.toBeNumber(), + returnString: expect.toBeString(), + returnFloat: expect.toBeNumber(), + returnBoolean: expect.toBeBoolean(), + returnID: expect.toBeString() + }); + expect(resp.data.returnInt % 1 === 0).toBe(true); + expect(resp.data.returnFloat % 1 !== 0).toBe(true); + }); + + test("can automock enums", () => { + const testQuery = /* GraphQL */ ` + { + returnEnum + } + `; + const resp: any = mock(schema, testQuery); + expect(resp.data).toMatchObject({ + returnEnum: expect.toBeOneOf(["A", "B", "C"]) + }); + }); + + test("can automock unions", () => { + const testQuery = /* GraphQL */ ` + { + returnBirdsAndBees { + __typename + ... on Bird { + returnInt + returnString + } + ... on Bee { + returnInt + returnEnum + } + } + } + `; + const resp: any = mock(schema, testQuery); + expect(resp.data.returnBirdsAndBees.length).toBeGreaterThan(0); + expect(resp.data.returnBirdsAndBees.length).toBeLessThan(5); + const firstType = resp.data.returnBirdsAndBees[0]; + expect(firstType.returnInt).toBeNumber(); + if (firstType.__typename === "Bird") { + expect(firstType.returnString).toBeString(); + } else { + expect(firstType.returnEnum).toBeOneOf(["A", "B", "C"]); + } + }); + + test("can automock interfaces", () => { + const testQuery = /* GraphQL */ ` + { + returnFlying { + __typename + ... on Bird { + returnInt + returnString + } + ... on Bee { + returnInt + returnEnum + } + } + } + `; + const resp: any = mock(schema, testQuery); + expect(resp.data.returnFlying.length).toBeGreaterThan(0); + expect(resp.data.returnFlying.length).toBeLessThan(5); + const firstType = resp.data.returnFlying[0]; + expect(firstType.returnInt).toBeNumber(); + if (firstType.__typename === "Bird") { + expect(firstType.returnString).toBeString(); + } else { + expect(firstType.returnEnum).toBeOneOf(["A", "B", "C"]); + } + }); + + test.todo("automocking of lists are deterministic on some seed"); + + test.todo("can automock objects"); + + test.todo("can automock nested unions"); + test.todo("can automock nested interfaces"); + test.todo("can automock nested basic types"); + test.todo("can automock nested enums"); + test.todo("can automock nested objects"); + test.todo("can automock inline fragments"); + + test.todo("can provide field mock override"); + }); + + describe("With partial mocks provided", () => { + test("can return string mock", () => { + // Given a query + const query = /* GraphQL */ ` + query SampleQuery { + returnInt + returnString + } + `; + + // And a partial mock + const mocks = { returnString: "bar" }; + const resp: any = mock(schema, query, mocks); + + // Return a fully mocked response + expect(resp).toMatchObject({ + data: { + returnInt: expect.toBeNumber(), + returnString: "bar" + } + }); + }); + + test.todo("can return basic types in mock"); + test.todo("can return basic types list in mock"); + test.todo("can return basic enum mock"); + test.todo("can return basic enum list mock"); + test.todo("throws if provided enum mock is invalid"); + test.todo("can return object mock"); + test.todo("can return object mock list"); + test.todo("can return union mock list"); + test.todo("can return interface mock list"); + test.todo("can return partial inline fragment mock"); + test.todo("can return provided nested basic types"); + test.todo("can return provided nested basic types list"); + test.todo("can return provided nested enums"); + test.todo("can return provided nested enums list"); + test.todo("can return provided nested unions"); + test.todo("can return provided nested interfaces"); + test.todo("executes functions when provided, with variables as args"); + }); + + describe("mocking errors", () => { + test.todo("can provide Errors for basic types"); + test.todo("can provide Errors for enums"); + test.todo("can provide Errors for objects"); + test.todo("can provide Errors for lists (one error among list items)"); + test.todo("can provide Errors for unions"); + test.todo("can provide Errors for interfaces"); + test.todo("can throw errors in functions as resolver"); + }); + + test("base case - TBD remove this test later", () => { + // Given a query + const query = /* GraphQL */ ` + query SampleQuery { + returnInt + returnString + returnFlying { + __typename + id + ... on Bird { + returnString + } + ... on Bee { + returnEnum + } + returnInt + } + } + `; + + // And a partial mock + const mocks = { + returnString: "bar", + returnFlying: [ + { __typename: "Bee", id: "123" }, + { __typename: "Bee" }, + { __typename: "Bird" } + ] + }; + + const resp: any = mock(schema, query, mocks); + + // Return a fully mocked response + expect(resp).toMatchObject({ + data: { + returnInt: expect.toBeNumber(), + returnString: "bar", + returnFlying: [ + { __typename: "Bee", id: "123", returnEnum: expect.toBeOneOf(["A", "B", "C"]) }, + { + __typename: "Bee", + id: expect.toBeString(), + returnEnum: expect.toBeOneOf(["A", "B", "C"]) + }, + { __typename: "Bird", id: expect.toBeString(), returnString: expect.toBeString() } + ] + } + }); + }); }); diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..3b47093 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1 @@ +import "jest-extended"; diff --git a/src/index.ts b/src/index.ts index cf84414..9cbd140 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,193 @@ -export default function main() { - return true; +import { + GraphQLSchema, + parse, + execute, + DocumentNode, + visitWithTypeInfo, + TypeInfo, + GraphQLType, + FieldNode, + GraphQLObjectType, + visit, + Kind, + GraphQLInterfaceType, + GraphQLUnionType, + getNamedType, + GraphQLField, + getNullableType, + GraphQLNullableType, + GraphQLResolveInfo, + GraphQLFieldResolver, + GraphQLList, + GraphQLEnumType, + isAbstractType, + isObjectType +} from "graphql"; +import { getFieldDef } from "graphql/execution/execute"; + +type IteratorFn = (fieldDef: GraphQLField, parentType: string, fieldName: string) => void; + +function forEachFieldInQuery(schema: GraphQLSchema, document: DocumentNode, fn: IteratorFn) { + const typeInfo = new TypeInfo(schema); + visit( + document, + visitWithTypeInfo(typeInfo, { + [Kind.FIELD](node: FieldNode): FieldNode | null | undefined { + const fieldName = node.name.value; + if (fieldName === "__typename") { + return; + } + const parentType = typeInfo.getParentType(); + // const fieldType = typeInfo.getType(); // the return type of this field. + if (isAbstractType(parentType)) { + const possibleTypes = schema.getPossibleTypes(parentType); + possibleTypes.forEach(t => { + const fieldDef = getFieldDef(schema, t, fieldName); + if (fieldDef) { + fn(fieldDef, t.name, fieldName); + } + }); + } + if (isObjectType(parentType)) { + const parentFields = parentType.getFields(); + const fieldDef = parentFields[node.name.value]; // the schame field definition + fn(fieldDef, parentType.name, fieldName); + } + } + }) + ); +} + +function assignResolveType(type: GraphQLType) { + const fieldType = getNullableType(type) as GraphQLNullableType; + const namedFieldType = getNamedType(fieldType); + + if ( + namedFieldType instanceof GraphQLUnionType || + namedFieldType instanceof GraphQLInterfaceType + ) { + // the default `resolveType` always returns null. We add a fallback + // resolution that works with how unions and interface are mocked + namedFieldType.resolveType = (data: any, context: any, info: GraphQLResolveInfo) => { + return info.schema.getType(data.__typename) as GraphQLObjectType; + }; + } +} + +function getRandomElement(ary: ReadonlyArray) { + const sample = Math.floor(Math.random() * ary.length); + return ary[sample]; +} + +const defaultMockMap: Map> = new Map(); +defaultMockMap.set("Int", () => Math.round(Math.random() * 200) - 100); +defaultMockMap.set("Float", () => Math.random() * 200 - 100); +defaultMockMap.set("String", () => "Hello World"); +defaultMockMap.set("Boolean", () => Math.random() > 0.5); +defaultMockMap.set("ID", () => "123456"); + +export function mock(schema: GraphQLSchema, query: string, partialMock?: any, options?: any) { + const document = parse(query); + + const mockResolverFunction = function(type: GraphQLType, typeName?: string, fieldName?: string) { + // order of precendence for mocking: + // 1. if the object passed in already has fieldName, just use that + // --> if it's a function, that becomes your resolver + // --> if it's a value, the mock resolver will return that + // 2. if the nullableType is a list, recurse + // 2. if there's a mock defined for this typeName, that will be used + // 3. if there's no mock defined, use the default mocks for this type + return ( + root: any, + args: { [key: string]: any }, + context: any, + info: GraphQLResolveInfo + ): any => { + // nullability doesn't matter for the purpose of mocking. + const fieldType = getNullableType(type) as GraphQLNullableType; + const namedFieldType = getNamedType(fieldType); + + if (root && fieldName && typeof root[fieldName] !== "undefined") { + // // if we're here, the field is already defined on the root object so use it + // if (typeof root[fieldName] === "function") { + // result = root[fieldName](root, args, context, info); + // } else { + // result = root[fieldName]; + // } + + return root[fieldName]; + } + + // Default mock for enums + if (fieldType instanceof GraphQLEnumType) { + return getRandomElement(fieldType.getValues()).value; + } + + // Lists + if (fieldType instanceof GraphQLList) { + return [ + mockResolverFunction(fieldType.ofType)(root, args, context, info), + mockResolverFunction(fieldType.ofType)(root, args, context, info) + ]; + } + + // Unions and interfaces + if (fieldType instanceof GraphQLUnionType || fieldType instanceof GraphQLInterfaceType) { + let implementationType; + const possibleTypes = schema.getPossibleTypes(fieldType); + implementationType = getRandomElement(possibleTypes); + return Object.assign( + { __typename: implementationType }, + mockResolverFunction(implementationType)(root, args, context, info) + ); + } + + // Mock default scalars + if (defaultMockMap.has(fieldType.name)) { + return defaultMockMap.get(fieldType.name)!(root, args, context, info); + } + }; + }; + + forEachFieldInQuery(schema, document, (field, typeName, fieldName) => { + assignResolveType(field.type); // assign the default .resolveType resolver. + let mockResolver: GraphQLFieldResolver; + + // we have to handle the root mutation and root query types differently, + // because no resolver is called at the root. + /* istanbul ignore next: Must provide schema DefinitionNode with query type or a type named Query. */ + const isOnQueryType: boolean = !!( + schema.getQueryType() && schema.getQueryType()?.name === typeName + ); + const isOnMutationType: boolean = !!( + schema.getMutationType() && schema.getMutationType()?.name === typeName + ); + + if (isOnQueryType || isOnMutationType) { + mockResolver = ( + root: any, + args: { [key: string]: any }, + context: any, + info: GraphQLResolveInfo + ) => { + return mockResolverFunction(field.type, typeName, fieldName)( + partialMock, + args, + context, + info + ); + }; + } else { + mockResolver = mockResolverFunction(field.type, typeName, fieldName); + } + field.resolve = mockResolver; + }); + + const resp = execute({ + schema, + document, + rootValue: {}, + contextValue: {} + }); + return resp; } diff --git a/tsconfig.json b/tsconfig.json index 6c6b8b5..996a9fc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -34,7 +34,7 @@ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ /* Additional Checks */ - "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ diff --git a/yarn.lock b/yarn.lock index 2bacc69..00ce917 100644 --- a/yarn.lock +++ b/yarn.lock @@ -182,6 +182,15 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== +"@jest/console@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" + integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== + dependencies: + "@jest/source-map" "^24.9.0" + chalk "^2.0.1" + slash "^2.0.0" + "@jest/console@^25.1.0": version "25.1.0" resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.1.0.tgz#1fc765d44a1e11aec5029c08e798246bd37075ab" @@ -279,6 +288,15 @@ optionalDependencies: node-notifier "^6.0.0" +"@jest/source-map@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" + integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== + dependencies: + callsites "^3.0.0" + graceful-fs "^4.1.15" + source-map "^0.6.0" + "@jest/source-map@^25.1.0": version "25.1.0" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.1.0.tgz#b012e6c469ccdbc379413f5c1b1ffb7ba7034fb0" @@ -288,6 +306,15 @@ graceful-fs "^4.2.3" source-map "^0.6.0" +"@jest/test-result@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" + integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== + dependencies: + "@jest/console" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@jest/test-result@^25.1.0": version "25.1.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.1.0.tgz#847af2972c1df9822a8200457e64be4ff62821f7" @@ -331,6 +358,15 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" +"@jest/types@^24.9.0": + version "24.9.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.9.0.tgz#63cb26cb7500d069e5a389441a7c6ab5e909fc59" + integrity sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^13.0.0" + "@jest/types@^25.1.0": version "25.1.0" resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.1.0.tgz#b26831916f0d7c381e11dbb5e103a72aed1b4395" @@ -642,6 +678,13 @@ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== +"@types/yargs@^13.0.0": + version "13.0.8" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-13.0.8.tgz#a38c22def2f1c2068f8971acb3ea734eb3c64a99" + integrity sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA== + dependencies: + "@types/yargs-parser" "*" + "@types/yargs@^15.0.0": version "15.0.4" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.4.tgz#7e5d0f8ca25e9d5849f2ea443cf7c402decd8299" @@ -649,6 +692,13 @@ dependencies: "@types/yargs-parser" "*" +"@wry/equality@^0.1.2": + version "0.1.9" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.9.tgz#b13e18b7a8053c6858aa6c85b54911fb31e3a909" + integrity sha512-mB6ceGjpMGz1ZTza8HYnrPGos2mC6So4NhS1PtZ8s4Qt0K7fBiIGhpSxUbQmhwcSWE3no+bYxmI2OL6KuXYmoQ== + dependencies: + tslib "^1.9.3" + JSONStream@^1.0.4, JSONStream@^1.3.4, JSONStream@^1.3.5: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" @@ -760,7 +810,7 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-regex@^4.1.0: +ansi-regex@^4.0.0, ansi-regex@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== @@ -770,7 +820,7 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -811,6 +861,26 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +apollo-link@^1.2.3: + version "1.2.13" + resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.13.tgz#dff00fbf19dfcd90fddbc14b6a3f9a771acac6c4" + integrity sha512-+iBMcYeevMm1JpYgwDEIDt/y0BB7VWyvlm/7x+TIPNLHCTCMgcEgDuW5kH86iQZWo0I7mNwQiTOz+/3ShPFmBw== + dependencies: + apollo-utilities "^1.3.0" + ts-invariant "^0.4.0" + tslib "^1.9.3" + zen-observable-ts "^0.8.20" + +apollo-utilities@^1.0.1, apollo-utilities@^1.3.0: + version "1.3.3" + resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.3.tgz#f1854715a7be80cd810bc3ac95df085815c0787c" + integrity sha512-F14aX2R/fKNYMvhuP2t9GD9fggID7zp5I96MF5QeKYWDWTrkRdHRp4+SVfXUVN+cXOaB/IebfvRtzPf25CM0zw== + dependencies: + "@wry/equality" "^0.1.2" + fast-json-stable-stringify "^2.0.0" + ts-invariant "^0.4.0" + tslib "^1.10.0" + aproba@^1.0.3, aproba@^1.1.1, aproba@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -1752,6 +1822,11 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= +deprecated-decorator@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37" + integrity sha1-AJZjF7ehL+kvPMgx91g68ym4bDc= + deprecation@^2.0.0, deprecation@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" @@ -1780,6 +1855,11 @@ dezalgo@^1.0.0, dezalgo@~1.0.3: asap "^2.0.0" wrappy "1" +diff-sequences@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" + integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== + diff-sequences@^25.1.0: version "25.1.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.1.0.tgz#fd29a46f1c913fd66c22645dc75bffbe43051f32" @@ -2054,6 +2134,18 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" +expect@^24.1.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" + integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== + dependencies: + "@jest/types" "^24.9.0" + ansi-styles "^3.2.0" + jest-get-type "^24.9.0" + jest-matcher-utils "^24.9.0" + jest-message-util "^24.9.0" + jest-regex-util "^24.9.0" + expect@^25.1.0: version "25.1.0" resolved "https://registry.yarnpkg.com/expect/-/expect-25.1.0.tgz#7e8d7b06a53f7d66ec927278db3304254ee683ee" @@ -2480,6 +2572,24 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +graphql-tools@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-4.0.7.tgz#743309b96cb657ff45b607ee0a07193cd987e43c" + integrity sha512-rApl8sT8t/W1uQRcwzxMYyUBiCl/XicluApiDkNze5TX/GR0BSTQMjM2UcRGdTmkbsb1Eqq6afkyyeG/zMxZYQ== + dependencies: + apollo-link "^1.2.3" + apollo-utilities "^1.0.1" + deprecated-decorator "^0.1.6" + iterall "^1.1.3" + uuid "^3.1.0" + +graphql@^14.6.0: + version "14.6.0" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.6.0.tgz#57822297111e874ea12f5cd4419616930cd83e49" + integrity sha512-VKzfvHEKybTKjQVpTFrA5yUq2S9ihcZvfJAtsDBBCuV6wauPu1xl/f9ehgVf0FcEJJs4vz6ysb/ZMkGigQZseg== + 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" @@ -3129,6 +3239,11 @@ istanbul-reports@^3.0.0: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" +iterall@^1.1.3, iterall@^1.2.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" + integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== + java-properties@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/java-properties/-/java-properties-1.0.2.tgz#ccd1fa73907438a5b5c38982269d0e771fe78211" @@ -3185,6 +3300,16 @@ jest-config@^25.1.0: pretty-format "^25.1.0" realpath-native "^1.1.0" +jest-diff@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" + integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== + dependencies: + chalk "^2.0.1" + diff-sequences "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + jest-diff@^25.1.0: version "25.1.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.1.0.tgz#58b827e63edea1bc80c1de952b80cec9ac50e1ad" @@ -3236,6 +3361,25 @@ jest-environment-node@^25.1.0: jest-mock "^25.1.0" jest-util "^25.1.0" +jest-extended@^0.11.5: + version "0.11.5" + resolved "https://registry.yarnpkg.com/jest-extended/-/jest-extended-0.11.5.tgz#f063b3f1eaadad8d7c13a01f0dfe0f538d498ccf" + integrity sha512-3RsdFpLWKScpsLD6hJuyr/tV5iFOrw7v6YjA3tPdda9sJwoHwcMROws5gwiIZfcwhHlJRwFJB2OUvGmF3evV/Q== + dependencies: + expect "^24.1.0" + jest-get-type "^22.4.3" + jest-matcher-utils "^22.0.0" + +jest-get-type@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" + integrity sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w== + +jest-get-type@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" + integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== + jest-get-type@^25.1.0: version "25.1.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.1.0.tgz#1cfe5fc34f148dc3a8a3b7275f6b9ce9e2e8a876" @@ -3290,6 +3434,25 @@ jest-leak-detector@^25.1.0: jest-get-type "^25.1.0" pretty-format "^25.1.0" +jest-matcher-utils@^22.0.0: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz#4632fe428ebc73ebc194d3c7b65d37b161f710ff" + integrity sha512-lsEHVaTnKzdAPR5t4B6OcxXo9Vy4K+kRRbG5gtddY8lBEC+Mlpvm1CJcsMESRjzUhzkz568exMV1hTB76nAKbA== + dependencies: + chalk "^2.0.1" + jest-get-type "^22.4.3" + pretty-format "^22.4.3" + +jest-matcher-utils@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" + integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== + dependencies: + chalk "^2.0.1" + jest-diff "^24.9.0" + jest-get-type "^24.9.0" + pretty-format "^24.9.0" + jest-matcher-utils@^25.1.0: version "25.1.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.1.0.tgz#fa5996c45c7193a3c24e73066fc14acdee020220" @@ -3300,6 +3463,20 @@ jest-matcher-utils@^25.1.0: jest-get-type "^25.1.0" pretty-format "^25.1.0" +jest-message-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" + integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@jest/test-result" "^24.9.0" + "@jest/types" "^24.9.0" + "@types/stack-utils" "^1.0.1" + chalk "^2.0.1" + micromatch "^3.1.10" + slash "^2.0.0" + stack-utils "^1.0.1" + jest-message-util@^25.1.0: version "25.1.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.1.0.tgz#702a9a5cb05c144b9aa73f06e17faa219389845e" @@ -3326,6 +3503,11 @@ jest-pnp-resolver@^1.2.1: resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== +jest-regex-util@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" + integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== + jest-regex-util@^25.1.0: version "25.1.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.1.0.tgz#efaf75914267741838e01de24da07b2192d16d87" @@ -4115,7 +4297,7 @@ merge2@^1.3.0: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== -micromatch@^3.1.4: +micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -5131,6 +5313,24 @@ prettier@1.19.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== +pretty-format@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f" + integrity sha512-S4oT9/sT6MN7/3COoOy+ZJeA92VmOnveLHgrwBE3Z1W5N9S2A1QGNYiE1z75DAENbJrXXUb+OWXhpJcg05QKQQ== + dependencies: + ansi-regex "^3.0.0" + ansi-styles "^3.2.0" + +pretty-format@^24.9.0: + version "24.9.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.9.0.tgz#12fac31b37019a4eea3c11aa9a959eb7628aa7c9" + integrity sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA== + dependencies: + "@jest/types" "^24.9.0" + ansi-regex "^4.0.0" + ansi-styles "^3.2.0" + react-is "^16.8.4" + pretty-format@^25.1.0: version "25.1.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.1.0.tgz#ed869bdaec1356fc5ae45de045e2c8ec7b07b0c8" @@ -5275,7 +5475,7 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.8: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-is@^16.12.0: +react-is@^16.12.0, react-is@^16.8.4: version "16.13.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.0.tgz#0f37c3613c34fe6b37cd7f763a0d6293ab15c527" integrity sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA== @@ -5820,6 +6020,11 @@ sisteransi@^1.0.4: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.4.tgz#386713f1ef688c7c0304dc4c0632898941cad2e3" integrity sha512-/ekMoM4NJ59ivGSfKapeG+FWtrmWvA1p6FBZwXrqojw90vJu8lBmrTxCMuBCydKtkaUe2zt4PlxeTKpjwMbyig== +slash@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" + integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -6412,6 +6617,13 @@ trim-off-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= +ts-invariant@^0.4.0: + version "0.4.4" + resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.4.4.tgz#97a523518688f93aafad01b0e80eb803eb2abd86" + integrity sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA== + dependencies: + tslib "^1.9.3" + ts-jest@25.2.1: version "25.2.1" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-25.2.1.tgz#49bf05da26a8b7fbfbc36b4ae2fcdc2fef35c85d" @@ -6428,6 +6640,11 @@ ts-jest@25.2.1: semver "^5.5" yargs-parser "^16.1.0" +tslib@^1.10.0, tslib@^1.9.3: + version "1.11.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" + integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -6654,7 +6871,7 @@ util.promisify@^1.0.0: has-symbols "^1.0.1" object.getownpropertydescriptors "^2.1.0" -uuid@^3.3.2, uuid@^3.3.3: +uuid@^3.1.0, uuid@^3.3.2, uuid@^3.3.3: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -6980,3 +7197,16 @@ yargs@^8.0.2: which-module "^2.0.0" y18n "^3.2.1" yargs-parser "^7.0.0" + +zen-observable-ts@^0.8.20: + version "0.8.20" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.20.tgz#44091e335d3fcbc97f6497e63e7f57d5b516b163" + integrity sha512-2rkjiPALhOtRaDX6pWyNqK1fnP5KkJJybYebopNSn6wDG1lxBoFs2+nwwXKoA6glHIrtwrfBBy6da0stkKtTAA== + dependencies: + tslib "^1.9.3" + zen-observable "^0.8.0" + +zen-observable@^0.8.0: + version "0.8.15" + resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" + integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==