From 23e9b25c2aa5bf154a832e638df8cea889cedd61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lytek?= Date: Sun, 1 Dec 2019 15:53:56 +0100 Subject: [PATCH] tests(subscriptions): refactor tests from apollo to async-iterators --- tests/functional/subscriptions.ts | 238 +++++++++++------- tests/helpers/sleep.ts | 3 + .../subscriptions/createWebSocketGQL.ts | 36 --- .../subscriptions/subscribeToPromise.ts | 12 - 4 files changed, 157 insertions(+), 132 deletions(-) create mode 100644 tests/helpers/sleep.ts delete mode 100644 tests/helpers/subscriptions/createWebSocketGQL.ts delete mode 100644 tests/helpers/subscriptions/subscribeToPromise.ts diff --git a/tests/functional/subscriptions.ts b/tests/functional/subscriptions.ts index 4ff46d9b8..4bbef5a42 100644 --- a/tests/functional/subscriptions.ts +++ b/tests/functional/subscriptions.ts @@ -5,11 +5,13 @@ import { IntrospectionSchema, TypeKind, graphql, + subscribe, + ExecutionResult, + execute, + DocumentNode, } from "graphql"; -import { ApolloClient } from "apollo-client"; import gql from "graphql-tag"; import { EventEmitter } from "events"; -import { ApolloServer } from "apollo-server"; import { PubSub as LocalPubSub } from "graphql-subscriptions"; import { @@ -31,7 +33,7 @@ import { import { getMetadataStorage } from "../../src/metadata/getMetadataStorage"; import { getSchemaInfo } from "../helpers/getSchemaInfo"; import { getInnerTypeOfNonNullableType, getItemTypeOfList } from "../helpers/getInnerFieldType"; -import createWebSocketUtils from "../helpers/subscriptions/createWebSocketGQL"; +import sleep from "../helpers/sleep"; describe("Subscriptions", () => { describe("Schema", () => { @@ -125,8 +127,6 @@ describe("Subscriptions", () => { describe("Functional", () => { let schema: GraphQLSchema; - let apolloClient: ApolloClient; - let apolloServer: ApolloServer; const localPubSub: PubSubEngine = new LocalPubSub(); beforeAll(async () => { @@ -223,22 +223,37 @@ describe("Subscriptions", () => { schema = await buildSchema({ resolvers: [SampleResolver], }); - - ({ apolloClient, apolloServer } = await createWebSocketUtils(schema)); - }); - - afterAll(async () => { - await apolloServer.stop(); }); it("should build schema without errors", async () => { expect(schema).toBeDefined(); }); + async function subscribeOnceAndMutate(options: { + mutation: DocumentNode; + mutationVariables?: object; + subscription: DocumentNode; + subscriptionVariables?: object; + onSubscribedData: (data: any) => void; + }) { + const results = (await subscribe( + schema, + options.subscription, + null, + null, + options.subscriptionVariables, + )) as AsyncIterableIterator>; + const onDataPromise = results.next().then(async ({ value }) => { + options.onSubscribedData(value.data); + }); + await execute(schema, options.mutation, null, null, options.mutationVariables); + await onDataPromise; + } + it("should successfully get data from subscription after publishing mutation", async () => { let subscriptionValue!: number; const testedValue = Math.PI; - const subscriptionQuery = gql` + const subscription = gql` subscription { sampleTopicSubscription { value @@ -251,10 +266,13 @@ describe("Subscriptions", () => { } `; - apolloClient.subscribe({ query: subscriptionQuery }).subscribe({ - next: ({ data }) => (subscriptionValue = data!.sampleTopicSubscription.value), + await subscribeOnceAndMutate({ + subscription, + mutation, + onSubscribedData: data => { + subscriptionValue = data.sampleTopicSubscription.value; + }, }); - await apolloClient.mutate({ mutation }); expect(subscriptionValue).toEqual(testedValue); }); @@ -262,7 +280,7 @@ describe("Subscriptions", () => { it("should successfully get data from subscription using fragments", async () => { let subscriptionValue!: number; const testedValue = Math.PI; - const subscriptionQuery = gql` + const subscription = gql` fragment TestFragment on SampleObject { value } @@ -278,10 +296,13 @@ describe("Subscriptions", () => { } `; - apolloClient.subscribe({ query: subscriptionQuery }).subscribe({ - next: ({ data }) => (subscriptionValue = data!.sampleTopicSubscription.value), + await subscribeOnceAndMutate({ + subscription, + mutation, + onSubscribedData: data => { + subscriptionValue = data.sampleTopicSubscription.value; + }, }); - await apolloClient.mutate({ mutation }); expect(subscriptionValue).toEqual(testedValue); }); @@ -301,22 +322,34 @@ describe("Subscriptions", () => { } `; - apolloClient.subscribe({ query: subscriptionQuery }).subscribe({ - next: ({ data }) => (subscriptionValue = data!.sampleTopicSubscription.value), - }); + const subscription = (await subscribe({ + schema, + document: subscriptionQuery, + })) as AsyncIterableIterator; + // run subscription in a separate async "thread" + (async () => { + for await (const result of subscription) { + subscriptionValue = result.data!.sampleTopicSubscription.value; + } + })(); - await apolloClient.mutate({ mutation, variables: { value: 1.23 } }); + await execute({ schema, document: mutation, variableValues: { value: 1.23 } }); + await sleep(0); expect(subscriptionValue).toEqual(1.23); - await apolloClient.mutate({ mutation, variables: { value: 2.37 } }); + + await execute({ schema, document: mutation, variableValues: { value: 2.37 } }); + await sleep(0); expect(subscriptionValue).toEqual(2.37); - await apolloClient.mutate({ mutation, variables: { value: 4.53 } }); + + await execute({ schema, document: mutation, variableValues: { value: 4.53 } }); + await sleep(0); expect(subscriptionValue).toEqual(4.53); }); it("should successfully publish using Publisher injection", async () => { let subscriptionValue: number; const testedValue = Math.PI; - const subscriptionQuery = gql` + const subscription = gql` subscription { sampleTopicSubscription { value @@ -329,10 +362,13 @@ describe("Subscriptions", () => { } `; - apolloClient.subscribe({ query: subscriptionQuery }).subscribe({ - next: ({ data }) => (subscriptionValue = data!.sampleTopicSubscription.value), + await subscribeOnceAndMutate({ + subscription, + mutation, + onSubscribedData: data => { + subscriptionValue = data.sampleTopicSubscription.value; + }, }); - await apolloClient.mutate({ mutation }); expect(subscriptionValue!).toEqual(testedValue); }); @@ -357,15 +393,27 @@ describe("Subscriptions", () => { } `; - apolloClient.subscribe({ query: subscriptionQuery }).subscribe({ - next: ({ data }) => (subscriptionValue = data!.sampleTopicSubscription.value), - }); + const subscription = (await subscribe({ + schema, + document: subscriptionQuery, + })) as AsyncIterableIterator; + // run subscription in a separate async "thread" + (async () => { + for await (const result of subscription) { + subscriptionValue = result.data!.sampleTopicSubscription.value; + } + })(); - await apolloClient.mutate({ mutation: otherTopicMutation, variables: { value: 1.23 } }); + await execute({ schema, document: otherTopicMutation, variableValues: { value: 1.23 } }); + await sleep(0); expect(subscriptionValue).toBeUndefined(); - await apolloClient.mutate({ mutation: otherTopicMutation, variables: { value: 2.37 } }); + + await execute({ schema, document: otherTopicMutation, variableValues: { value: 2.37 } }); + await sleep(0); expect(subscriptionValue).toBeUndefined(); - await apolloClient.mutate({ mutation: sampleTopicMutation, variables: { value: 3.47 } }); + + await execute({ schema, document: sampleTopicMutation, variableValues: { value: 3.47 } }); + await sleep(0); expect(subscriptionValue).toEqual(3.47); }); @@ -384,15 +432,27 @@ describe("Subscriptions", () => { } `; - apolloClient.subscribe({ query: subscriptionQuery }).subscribe({ - next: ({ data }) => (subscriptionValue = data!.sampleTopicSubscriptionWithFilter.value), - }); + const subscription = (await subscribe({ + schema, + document: subscriptionQuery, + })) as AsyncIterableIterator; + // run subscription in a separate async "thread" + (async () => { + for await (const result of subscription) { + subscriptionValue = result.data!.sampleTopicSubscriptionWithFilter.value; + } + })(); - await apolloClient.mutate({ mutation, variables: { value: 0.23 } }); + await execute({ schema, document: mutation, variableValues: { value: 0.23 } }); + await sleep(0); expect(subscriptionValue).toBeUndefined(); - await apolloClient.mutate({ mutation, variables: { value: 0.77 } }); + + await execute({ schema, document: mutation, variableValues: { value: 0.77 } }); + await sleep(0); expect(subscriptionValue).toEqual(0.77); - await apolloClient.mutate({ mutation, variables: { value: 0.44 } }); + + await execute({ schema, document: mutation, variableValues: { value: 0.44 } }); + await sleep(0); expect(subscriptionValue).toEqual(0.77); }); @@ -416,15 +476,27 @@ describe("Subscriptions", () => { } `; - apolloClient.subscribe({ query: subscriptionQuery }).subscribe({ - next: ({ data }) => (subscriptionValue = data!.multipleTopicSubscription.value), - }); + const subscription = (await subscribe({ + schema, + document: subscriptionQuery, + })) as AsyncIterableIterator; + // run subscription in a separate async "thread" + (async () => { + for await (const result of subscription) { + subscriptionValue = result.data!.multipleTopicSubscription.value; + } + })(); - await apolloClient.mutate({ mutation: sampleTopicMutation, variables: { value: 0.23 } }); + await execute({ schema, document: sampleTopicMutation, variableValues: { value: 0.23 } }); + await sleep(0); expect(subscriptionValue).toEqual(0.23); - await apolloClient.mutate({ mutation: otherTopicMutation, variables: { value: 0.77 } }); + + await execute({ schema, document: otherTopicMutation, variableValues: { value: 0.77 } }); + await sleep(0); expect(subscriptionValue).toEqual(0.77); - await apolloClient.mutate({ mutation: sampleTopicMutation, variables: { value: 0.44 } }); + + await execute({ schema, document: sampleTopicMutation, variableValues: { value: 0.44 } }); + await sleep(0); expect(subscriptionValue).toEqual(0.44); }); @@ -444,22 +516,46 @@ describe("Subscriptions", () => { } `; - apolloClient - .subscribe({ - query: dynamicTopicSubscription, - variables: { topic: SAMPLE_TOPIC }, - }) - .subscribe({ - next: ({ data }) => (subscriptionValue = data!.dynamicTopicSubscription.value), - }); - - await apolloClient.mutate({ + await subscribeOnceAndMutate({ + subscription: dynamicTopicSubscription, + subscriptionVariables: { topic: SAMPLE_TOPIC }, mutation: pubSubMutationDynamicTopic, - variables: { value: 0.23, topic: SAMPLE_TOPIC }, + mutationVariables: { value: 0.23, topic: SAMPLE_TOPIC }, + onSubscribedData: data => { + subscriptionValue = data.dynamicTopicSubscription.value; + }, }); + expect(subscriptionValue).toEqual(0.23); }); + it("should correctly subscribe with custom subscribe function", async () => { + let subscriptionValue!: number; + const testedValue = Math.PI; + const subscription = gql` + subscription { + customSubscribeSubscription { + value + } + } + `; + const mutation = gql` + mutation { + pubSubMutationCustomSubscription(value: ${testedValue}) + } + `; + + await subscribeOnceAndMutate({ + subscription, + mutation, + onSubscribedData: data => { + subscriptionValue = data.customSubscribeSubscription.value; + }, + }); + + expect(subscriptionValue).toEqual(testedValue); + }); + it("should inject the provided custom PubSub implementation", async () => { let pubSub: any; getMetadataStorage().clear(); @@ -572,31 +668,5 @@ describe("Subscriptions", () => { expect(err.message).not.toContain("class SampleResolver"); } }); - - it("should correctly subscribe with custom subscribe function", async () => { - let subscriptionValue!: number; - const testedValue = Math.PI; - const subscriptionQuery = gql` - subscription { - customSubscribeSubscription { - value - } - } - `; - const mutation = gql` - mutation { - pubSubMutationCustomSubscription(value: ${testedValue}) - } - `; - - apolloClient.subscribe({ query: subscriptionQuery }).subscribe({ - next: ({ data }) => { - subscriptionValue = data!.customSubscribeSubscription.value; - }, - }); - await apolloClient.mutate({ mutation }); - - expect(subscriptionValue).toEqual(testedValue); - }); }); }); diff --git a/tests/helpers/sleep.ts b/tests/helpers/sleep.ts new file mode 100644 index 000000000..92793a163 --- /dev/null +++ b/tests/helpers/sleep.ts @@ -0,0 +1,3 @@ +export default function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/tests/helpers/subscriptions/createWebSocketGQL.ts b/tests/helpers/subscriptions/createWebSocketGQL.ts deleted file mode 100644 index 3c11ed6c3..000000000 --- a/tests/helpers/subscriptions/createWebSocketGQL.ts +++ /dev/null @@ -1,36 +0,0 @@ -import * as NodeWebSocket from "ws"; -import { GraphQLSchema } from "graphql"; -import { ApolloServer } from "apollo-server"; -import { SubscriptionClient } from "subscriptions-transport-ws"; -import { ApolloClient } from "apollo-client"; -import { WebSocketLink } from "apollo-link-ws"; -import { InMemoryCache } from "apollo-cache-inmemory"; - -export interface WebSocketUtils { - apolloClient: ApolloClient; - apolloServer: ApolloServer; -} - -export default async function createWebSocketUtils(schema: GraphQLSchema): Promise { - const apolloServer = new ApolloServer({ - schema, - playground: false, - }); - const { subscriptionsUrl } = await apolloServer.listen({ - port: 0, - }); - - const subscriptionClient = new SubscriptionClient( - subscriptionsUrl, - { reconnect: true }, - NodeWebSocket, - ); - const apolloClient = new ApolloClient({ - link: new WebSocketLink(subscriptionClient), - cache: new InMemoryCache(), - }); - return { - apolloClient, - apolloServer, - }; -} diff --git a/tests/helpers/subscriptions/subscribeToPromise.ts b/tests/helpers/subscriptions/subscribeToPromise.ts deleted file mode 100644 index 75c99fa6e..000000000 --- a/tests/helpers/subscriptions/subscribeToPromise.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DocumentNode, ExecutionResult } from "graphql"; -import { ApolloClient } from "apollo-client"; - -export interface Options { - query: DocumentNode; - apollo: ApolloClient; -} -export function apolloSubscribeToPromise({ apollo, query }: Options): Promise { - return new Promise((resolve, reject) => { - apollo.subscribe({ query }).subscribe({ next: resolve, error: reject }); - }); -}