Skip to content

Commit

Permalink
fix(mocking): support subscriptions (#1952)
Browse files Browse the repository at this point in the history
* fix(mocking): support subscriptions

* respect old subscriber
  • Loading branch information
ardatan committed Aug 30, 2020
1 parent ff71263 commit 570285b
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 3 deletions.
39 changes: 37 additions & 2 deletions packages/mock/src/mocking.ts
Expand Up @@ -237,7 +237,10 @@ export function addMocksToSchema({ schema, mocks = {}, preserveResolvers = false
const mutationType = schema.getMutationType();
const isOnMutationType = mutationType != null && mutationType.name === typeName;

if (isOnQueryType || isOnMutationType) {
const subscriptionType = schema.getSubscriptionType();
const isOnSubscriptionType = subscriptionType != null && subscriptionType.name === typeName;

if (isOnQueryType || isOnMutationType || isOnSubscriptionType) {
if (mockFunctionMap.has(typeName)) {
const rootMock = mockFunctionMap.get(typeName);
// XXX: BUG in here, need to provide proper signature for rootMock.
Expand All @@ -250,7 +253,8 @@ export function addMocksToSchema({ schema, mocks = {}, preserveResolvers = false
// otherwise we could just set field.resolve to rootMock()[fieldName]
// it's like pretending there was a resolver that ran before
// the root resolver.
return mockType(fieldConfig.type, typeName, fieldName)(updatedRoot, args, context, info);
const result = mockType(fieldConfig.type, typeName, fieldName)(updatedRoot, args, context, info);
return result;
};
}
}
Expand Down Expand Up @@ -289,6 +293,37 @@ export function addMocksToSchema({ schema, mocks = {}, preserveResolvers = false
});
}

const fieldSubscriber = fieldConfig.subscribe;
const mockSubscriber = (..._args: any[]) => ({
[Symbol.asyncIterator]() {
return {
async next() {
return {
done: true,
value: {},
};
},
};
},
});

if (!preserveResolvers || !fieldSubscriber) {
newFieldConfig.subscribe = mockSubscriber;
} else {
newFieldConfig.subscribe = async (
rootObject: any,
args: Record<string, any>,
context: any,
info: GraphQLResolveInfo
) => {
const [mockAsyncIterable, oldAsyncIterable] = await Promise.all([
mockSubscriber(rootObject, args, context, info),
fieldSubscriber(rootObject, args, context, info),
]);
return oldAsyncIterable || mockAsyncIterable;
};
}

return newFieldConfig;
},
});
Expand Down
40 changes: 39 additions & 1 deletion packages/mock/tests/mocking.spec.ts
Expand Up @@ -4,7 +4,7 @@ import {
GraphQLResolveInfo,
GraphQLSchema,
GraphQLFieldResolver,
buildSchema,
buildSchema, subscribe, parse
} from 'graphql';

import { sentence, first_name } from 'casual';
Expand Down Expand Up @@ -1616,6 +1616,44 @@ describe('Mock', () => {
});
});

it('resolves subscriptions only once', async () => {
let schema = buildSchema(/* GraphQL */ `
type Foo {
bar: String
}
type Query {
foo: Foo
}
type Subscription {
fooSub: Foo
}
`);

schema = addMocksToSchema({ schema });

const resultIterator = await subscribe({
schema,
document: /* GraphQL */ parse(`
subscription FooSub {
fooSub {
bar
}
}
`),
});

expect(resultIterator[Symbol.asyncIterator]).toBeTruthy();

for await (const result of resultIterator as any) {
expect(result).toBe({
fooSub: {
bar: 'Hello World!'
}
})
}

})

// TODO add a test that checks that even when merging defaults, lists invoke
// the function for every object, not just once per list.

Expand Down

0 comments on commit 570285b

Please sign in to comment.