diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22703722a64..d1c7a799ffd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -209,6 +209,9 @@
- Support passing a `context` object through the link execution chain when using subscriptions.
[@sgtpepper43](https://github.com/sgtpepper43) in [#4925](https://github.com/apollographql/apollo-client/pull/4925)
+- `MockSubscriptionLink` now supports multiple subscriptions.
+ [@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.
diff --git a/src/utilities/testing/mocking/__tests__/mockSubscriptionLink.test.tsx b/src/utilities/testing/mocking/__tests__/mockSubscriptionLink.test.tsx
new file mode 100644
index 00000000000..eeaa8d1e2f9
--- /dev/null
+++ b/src/utilities/testing/mocking/__tests__/mockSubscriptionLink.test.tsx
@@ -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(
+
+
+
+
+
+
+
+ );
+
+ return wait(() => {
+ expect(renderCountA).toBe(results.length + 1);
+ expect(renderCountB).toBe(results.length + 1);
+ }, { timeout: 1000 });
+ });
+});
diff --git a/src/utilities/testing/mocking/mockSubscriptionLink.ts b/src/utilities/testing/mocking/mockSubscriptionLink.ts
index 78956933501..642499db788 100644
--- a/src/utilities/testing/mocking/mockSubscriptionLink.ts
+++ b/src/utilities/testing/mocking/mockSubscriptionLink.ts
@@ -17,7 +17,7 @@ export class MockSubscriptionLink extends ApolloLink {
public setups: any[] = [];
public operation: Operation;
- private observer: any;
+ private observers: any[] = [];
constructor() {
super();
@@ -27,7 +27,7 @@ export class MockSubscriptionLink extends ApolloLink {
this.operation = operation;
return new Observable(observer => {
this.setups.forEach(x => x());
- this.observer = observer;
+ this.observers.push(observer);
return () => {
this.unsubscribers.forEach(x => x());
};
@@ -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 {