Skip to content

Commit

Permalink
Merge pull request #6081 from dfrankland/fix-mocksubscriptionlink-mul…
Browse files Browse the repository at this point in the history
…tiple-subscriptions

Allow multiple subscribers to a MockSubscriptionLink
  • Loading branch information
hwillson authored Jul 10, 2020
2 parents ede4b31 + 2c34086 commit 190a7f6
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 10 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@
- Support passing a `context` object through the link execution chain when using subscriptions. <br/>
[@sgtpepper43](https://github.com/sgtpepper43) in [#4925](https://github.com/apollographql/apollo-client/pull/4925)

- `MockSubscriptionLink` now supports multiple subscriptions. <br/>
[@dfrankland](https://github.com/dfrankland) in [#6081](https://github.com/apollographql/apollo-client/pull/6081)

### Bug Fixes

- `useMutation` adjustments to help avoid an infinite loop / too many renders issue, caused by unintentionally modifying the `useState` based mutation result directly. <br/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react';
import { render, wait } from '@testing-library/react';
import gql from 'graphql-tag';

import { MockSubscriptionLink } from '../mockSubscriptionLink';
import { ApolloClient } from '../../../../ApolloClient';
import { InMemoryCache as Cache } from '../../../../cache/inmemory/inMemoryCache';
import { ApolloProvider } from '../../../../react/context/ApolloProvider';
import { useSubscription } from '../../../../react/hooks/useSubscription';

describe('mockSubscriptionLink', () => {
it('should work with multiple subscribers to the same mock websocket', () => {
const subscription = gql`
subscription {
car {
make
}
}
`;

const link = new MockSubscriptionLink();
const client = new ApolloClient({
link,
cache: new Cache({ addTypename: false })
});

let renderCountA = 0;
const ComponentA = () => {
useSubscription(subscription);
renderCountA += 1;
return null;
};

let renderCountB = 0;
const ComponentB = () => {
useSubscription(subscription);
renderCountB += 1;
return null;
};

const results = ['Audi', 'BMW', 'Mercedes', 'Hyundai'].map(make => ({
result: { data: { car: { make } } }
}));

const Component = () => {
const [index, setIndex] = React.useState(0);
React.useEffect(() => {
if (index >= results.length) return;
link.simulateResult(results[index]);
setIndex(index + 1);
}, [index]);
return null;
};

render(
<ApolloProvider client={client}>
<div>
<Component />
<ComponentA />
<ComponentB />
</div>
</ApolloProvider>
);

return wait(() => {
expect(renderCountA).toBe(results.length + 1);
expect(renderCountB).toBe(results.length + 1);
}, { timeout: 1000 });
});
});
24 changes: 14 additions & 10 deletions src/utilities/testing/mocking/mockSubscriptionLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class MockSubscriptionLink extends ApolloLink {
public setups: any[] = [];
public operation: Operation;

private observer: any;
private observers: any[] = [];

constructor() {
super();
Expand All @@ -27,7 +27,7 @@ export class MockSubscriptionLink extends ApolloLink {
this.operation = operation;
return new Observable<FetchResult>(observer => {
this.setups.forEach(x => x());
this.observer = observer;
this.observers.push(observer);
return () => {
this.unsubscribers.forEach(x => x());
};
Expand All @@ -36,18 +36,22 @@ export class MockSubscriptionLink extends ApolloLink {

public simulateResult(result: MockedSubscriptionResult, complete = false) {
setTimeout(() => {
const { observer } = this;
if (!observer) throw new Error('subscription torn down');
if (complete && observer.complete) observer.complete();
if (result.result && observer.next) observer.next(result.result);
if (result.error && observer.error) observer.error(result.error);
const { observers } = this;
if (!observers.length) throw new Error('subscription torn down');
observers.forEach(observer => {
if (complete && observer.complete) observer.complete();
if (result.result && observer.next) observer.next(result.result);
if (result.error && observer.error) observer.error(result.error);
});
}, result.delay || 0);
}

public simulateComplete() {
const { observer } = this;
if (!observer) throw new Error('subscription torn down');
if (observer.complete) observer.complete();
const { observers } = this;
if (!observers.length) throw new Error('subscription torn down');
observers.forEach(observer => {
if (observer.complete) observer.complete();
})
}

public onSetup(listener: any): void {
Expand Down

0 comments on commit 190a7f6

Please sign in to comment.