diff --git a/src/link/batch/__tests__/batchLink.ts b/src/link/batch/__tests__/batchLink.ts index e5930924c27..ff5aa72edd0 100644 --- a/src/link/batch/__tests__/batchLink.ts +++ b/src/link/batch/__tests__/batchLink.ts @@ -4,13 +4,14 @@ import { print } from "graphql"; import { ApolloLink, execute } from "../../core"; import { Operation, FetchResult, GraphQLRequest } from "../../core/types"; import { Observable } from "../../../utilities"; -import { itAsync } from "../../../testing"; +import { wait } from "../../../testing"; import { BatchLink, OperationBatcher, BatchHandler, BatchableRequest, } from "../batchLink"; +import { ObservableStream } from "../../../testing/internal"; interface MockedResponse { request: GraphQLRequest; @@ -57,22 +58,6 @@ function createOperation(starting: any, operation: GraphQLRequest): Operation { return operation as Operation; } -function terminatingCheck( - resolve: () => any, - reject: (e: any) => any, - callback: (...args: TArgs) => any -) { - return function () { - try { - // @ts-expect-error - callback.apply(this, arguments); - resolve(); - } catch (error) { - reject(error); - } - } as typeof callback; -} - function requestToKey(request: GraphQLRequest): string { const queryString = typeof request.query === "string" ? request.query : print(request.query); @@ -221,195 +206,129 @@ describe("OperationBatcher", () => { } ); - itAsync( - "should be able to consume from a queue containing a single query", - (resolve, reject) => { - const myBatcher = new OperationBatcher({ - batchInterval: 10, - batchHandler, - }); + it("should be able to consume from a queue containing a single query", async () => { + const myBatcher = new OperationBatcher({ + batchInterval: 10, + batchHandler, + }); + + const observable = myBatcher.enqueueRequest({ operation }); + const stream = new ObservableStream(observable); + + const observables: (Observable | undefined)[] = + myBatcher.consumeQueue()!; - myBatcher.enqueueRequest({ operation }).subscribe( - terminatingCheck(resolve, reject, (resultObj: any) => { - expect(myBatcher["batchesByKey"].get("")).toBeUndefined(); - expect(resultObj).toEqual({ data }); - }) - ); - const observables: (Observable | undefined)[] = - myBatcher.consumeQueue()!; - - try { - expect(observables.length).toBe(1); - } catch (e) { - reject(e); + expect(observables.length).toBe(1); + expect(myBatcher["batchesByKey"].get("")).toBeUndefined(); + + await expect(stream).toEmitValue({ data }); + }); + + it("should be able to consume from a queue containing multiple queries", async () => { + const request2: Operation = createOperation( + {}, + { + query, } - } - ); + ); - itAsync( - "should be able to consume from a queue containing multiple queries", - (resolve, reject) => { - const request2: Operation = createOperation( - {}, - { - query, - } - ); + const BH = createMockBatchHandler( + { + request: { query }, + result: { data }, + }, + { + request: { query }, + result: { data }, + } + ); - const BH = createMockBatchHandler( - { - request: { query }, - result: { data }, - }, - { - request: { query }, - result: { data }, - } - ); + const myBatcher = new OperationBatcher({ + batchInterval: 10, + batchMax: 10, + batchHandler: BH, + }); + const observable1 = myBatcher.enqueueRequest({ operation }); + const observable2 = myBatcher.enqueueRequest({ operation: request2 }); - const myBatcher = new OperationBatcher({ - batchInterval: 10, - batchMax: 10, - batchHandler: BH, - }); - const observable1 = myBatcher.enqueueRequest({ operation }); - const observable2 = myBatcher.enqueueRequest({ operation: request2 }); - let notify = false; - observable1.subscribe((resultObj1) => { - try { - expect(resultObj1).toEqual({ data }); - } catch (e) { - reject(e); - } + const stream1 = new ObservableStream(observable1); + const stream2 = new ObservableStream(observable2); - if (notify) { - resolve(); - } else { - notify = true; - } - }); + expect(myBatcher["batchesByKey"].get("")!.size).toBe(2); + const observables: (Observable | undefined)[] = + myBatcher.consumeQueue()!; + expect(myBatcher["batchesByKey"].get("")).toBeUndefined(); + expect(observables.length).toBe(2); - observable2.subscribe((resultObj2) => { - try { - expect(resultObj2).toEqual({ data }); - } catch (e) { - reject(e); - } + await expect(stream1).toEmitValue({ data }); + await expect(stream2).toEmitValue({ data }); + }); - if (notify) { - resolve(); - } else { - notify = true; - } - }); + it("should be able to consume from a queue containing multiple queries with different batch keys", async () => { + // NOTE: this test was added to ensure that queries don't "hang" when consumed by BatchLink. + // "Hanging" in this case results in this test never resolving. So + // if this test times out it's probably a real issue and not a flake + const request2: Operation = createOperation( + {}, + { + query, + } + ); - try { - expect(myBatcher["batchesByKey"].get("")!.size).toBe(2); - const observables: (Observable | undefined)[] = - myBatcher.consumeQueue()!; - expect(myBatcher["batchesByKey"].get("")).toBeUndefined(); - expect(observables.length).toBe(2); - } catch (e) { - reject(e); + const BH = createMockBatchHandler( + { + request: { query }, + result: { data }, + }, + { + request: { query }, + result: { data }, } - } - ); + ); - itAsync( - "should be able to consume from a queue containing multiple queries with different batch keys", - (resolve, reject) => { - // NOTE: this test was added to ensure that queries don't "hang" when consumed by BatchLink. - // "Hanging" in this case results in this test never resolving. So - // if this test times out it's probably a real issue and not a flake - const request2: Operation = createOperation( - {}, - { - query, - } - ); + let key = true; + const batchKey = () => { + key = !key; + return "" + !key; + }; - const BH = createMockBatchHandler( - { - request: { query }, - result: { data }, - }, - { - request: { query }, - result: { data }, - } - ); - - let key = true; - const batchKey = () => { - key = !key; - return "" + !key; - }; - - const myBatcher = new OperationBatcher({ - batchInterval: 10, - batchMax: 10, - batchHandler: BH, - batchKey, - }); + const myBatcher = new OperationBatcher({ + batchInterval: 10, + batchMax: 10, + batchHandler: BH, + batchKey, + }); - const observable1 = myBatcher.enqueueRequest({ operation }); - const observable2 = myBatcher.enqueueRequest({ operation: request2 }); + const observable1 = myBatcher.enqueueRequest({ operation }); + const observable2 = myBatcher.enqueueRequest({ operation: request2 }); - let notify = false; - observable1.subscribe((resultObj1) => { - try { - expect(resultObj1).toEqual({ data }); - } catch (e) { - reject(e); - } + const stream1 = new ObservableStream(observable1); + const stream2 = new ObservableStream(observable2); - if (notify) { - resolve(); - } else { - notify = true; - } - }); + jest.runAllTimers(); - observable2.subscribe((resultObj2) => { - try { - expect(resultObj2).toEqual({ data }); - } catch (e) { - reject(e); - } + await expect(stream1).toEmitValue({ data }); + await expect(stream2).toEmitValue({ data }); + }); - if (notify) { - resolve(); - } else { - notify = true; - } - }); + it("should return a promise when we enqueue a request and resolve it with a result", async () => { + const BH = createMockBatchHandler({ + request: { query }, + result: { data }, + }); + const myBatcher = new OperationBatcher({ + batchInterval: 10, + batchHandler: BH, + }); + const observable = myBatcher.enqueueRequest({ operation }); + const stream = new ObservableStream(observable); - jest.runAllTimers(); - } - ); + myBatcher.consumeQueue(); - itAsync( - "should return a promise when we enqueue a request and resolve it with a result", - (resolve, reject) => { - const BH = createMockBatchHandler({ - request: { query }, - result: { data }, - }); - const myBatcher = new OperationBatcher({ - batchInterval: 10, - batchHandler: BH, - }); - const observable = myBatcher.enqueueRequest({ operation }); - observable.subscribe( - terminatingCheck(resolve, reject, (result: any) => { - expect(result).toEqual({ data }); - }) - ); - myBatcher.consumeQueue(); - } - ); + await expect(stream).toEmitValue({ data }); + }); - itAsync("should be able to debounce requests", (resolve, reject) => { + it("should be able to debounce requests", () => { const batchInterval = 10; const myBatcher = new OperationBatcher({ batchDebounce: true, @@ -442,11 +361,10 @@ describe("OperationBatcher", () => { // and expect the queue to be empty. jest.advanceTimersByTime(batchInterval / 2); expect(myBatcher["batchesByKey"].size).toEqual(0); - resolve(); }); }); - itAsync("should work when single query", (resolve, reject) => { + it("should work when single query", async () => { const data = { lastName: "Ever", firstName: "Greatest", @@ -470,152 +388,138 @@ describe("OperationBatcher", () => { const operation: Operation = createOperation({}, { query }); batcher.enqueueRequest({ operation }).subscribe({}); - try { - expect(batcher["batchesByKey"].get("")!.size).toBe(1); - } catch (e) { - reject(e); - } - - setTimeout( - terminatingCheck(resolve, reject, () => { - expect(batcher["batchesByKey"].get("")).toBeUndefined(); - }), - 20 - ); + expect(batcher["batchesByKey"].get("")!.size).toBe(1); + const promise = wait(20); jest.runAllTimers(); + await promise; + + expect(batcher["batchesByKey"].get("")).toBeUndefined(); }); - itAsync( - "should cancel single query in queue when unsubscribing", - (resolve, reject) => { - const data = { - lastName: "Ever", - firstName: "Greatest", - }; + it("should cancel single query in queue when unsubscribing", async () => { + const data = { + lastName: "Ever", + firstName: "Greatest", + }; - const batcher = new OperationBatcher({ - batchInterval: 10, - batchHandler: () => - new Observable((observer) => { - observer.next([{ data }]); - setTimeout(observer.complete.bind(observer)); - }), - }); + const batcher = new OperationBatcher({ + batchInterval: 10, + batchHandler: () => + new Observable((observer) => { + observer.next([{ data }]); + setTimeout(observer.complete.bind(observer)); + }), + }); - const query = gql` - query { - author { - firstName - lastName - } + const query = gql` + query { + author { + firstName + lastName } - `; + } + `; - batcher - .enqueueRequest({ - operation: createOperation({}, { query }), - }) - .subscribe(() => reject("next should never be called")) - .unsubscribe(); + batcher + .enqueueRequest({ + operation: createOperation({}, { query }), + }) + .subscribe(() => { + throw new Error("next should never be called"); + }) + .unsubscribe(); - expect(batcher["batchesByKey"].get("")).toBeUndefined(); - resolve(); - } - ); - - itAsync( - "should cancel single query in queue with multiple subscriptions", - (resolve, reject) => { - const data = { - lastName: "Ever", - firstName: "Greatest", - }; - const batcher = new OperationBatcher({ - batchInterval: 10, - batchHandler: () => - new Observable((observer) => { - observer.next([{ data }]); - setTimeout(observer.complete.bind(observer)); - }), - }); - const query = gql` - query { - author { - firstName - lastName - } + expect(batcher["batchesByKey"].get("")).toBeUndefined(); + }); + + it("should cancel single query in queue with multiple subscriptions", () => { + const data = { + lastName: "Ever", + firstName: "Greatest", + }; + const batcher = new OperationBatcher({ + batchInterval: 10, + batchHandler: () => + new Observable((observer) => { + observer.next([{ data }]); + setTimeout(observer.complete.bind(observer)); + }), + }); + const query = gql` + query { + author { + firstName + lastName } - `; - const operation: Operation = createOperation({}, { query }); + } + `; + const operation: Operation = createOperation({}, { query }); - const observable = batcher.enqueueRequest({ operation }); + const observable = batcher.enqueueRequest({ operation }); - const checkQueuedRequests = (expectedSubscriberCount: number) => { - const batch = batcher["batchesByKey"].get(""); - expect(batch).not.toBeUndefined(); - expect(batch!.size).toBe(1); - batch!.forEach((request) => { - expect(request.subscribers.size).toBe(expectedSubscriberCount); - }); - }; + const checkQueuedRequests = (expectedSubscriberCount: number) => { + const batch = batcher["batchesByKey"].get(""); + expect(batch).not.toBeUndefined(); + expect(batch!.size).toBe(1); + batch!.forEach((request) => { + expect(request.subscribers.size).toBe(expectedSubscriberCount); + }); + }; - const sub1 = observable.subscribe(() => - reject("next should never be called") - ); - checkQueuedRequests(1); + const sub1 = observable.subscribe(() => { + throw new Error("next should never be called"); + }); + checkQueuedRequests(1); - const sub2 = observable.subscribe(() => - reject("next should never be called") - ); - checkQueuedRequests(2); + const sub2 = observable.subscribe(() => { + throw new Error("next should never be called"); + }); + checkQueuedRequests(2); - sub1.unsubscribe(); - checkQueuedRequests(1); + sub1.unsubscribe(); + checkQueuedRequests(1); - sub2.unsubscribe(); - expect(batcher["batchesByKey"].get("")).toBeUndefined(); - resolve(); - } - ); + sub2.unsubscribe(); + expect(batcher["batchesByKey"].get("")).toBeUndefined(); + }); - itAsync( - "should cancel single query in flight when unsubscribing", - (resolve, reject) => { - const batcher = new OperationBatcher({ - batchInterval: 10, - batchHandler: () => - new Observable(() => { - // Instead of typically starting an XHR, we trigger the unsubscription from outside - setTimeout(() => subscription?.unsubscribe(), 5); - - return () => { - expect(batcher["batchesByKey"].get("")).toBeUndefined(); - resolve(); - }; - }), - }); + it("should cancel single query in flight when unsubscribing", (done) => { + const batcher = new OperationBatcher({ + batchInterval: 10, + batchHandler: () => + new Observable(() => { + // Instead of typically starting an XHR, we trigger the unsubscription from outside + setTimeout(() => subscription?.unsubscribe(), 5); + + return () => { + expect(batcher["batchesByKey"].get("")).toBeUndefined(); + done(); + }; + }), + }); - const query = gql` - query { - author { - firstName - lastName - } + const query = gql` + query { + author { + firstName + lastName } - `; + } + `; - const subscription = batcher - .enqueueRequest({ - operation: createOperation({}, { query }), - }) - .subscribe(() => reject("next should never be called")); + const subscription = batcher + .enqueueRequest({ + operation: createOperation({}, { query }), + }) + .subscribe(() => { + throw new Error("next should never be called"); + }); - jest.runAllTimers(); - } - ); + jest.runAllTimers(); + }); - itAsync("should correctly batch multiple queries", (resolve, reject) => { + it("should correctly batch multiple queries", async () => { const data = { lastName: "Ever", firstName: "Greatest", @@ -646,126 +550,109 @@ describe("OperationBatcher", () => { batcher.enqueueRequest({ operation }).subscribe({}); batcher.enqueueRequest({ operation: operation2 }).subscribe({}); - try { - expect(batcher["batchesByKey"].get("")!.size).toBe(2); - } catch (e) { - reject(e); - } + expect(batcher["batchesByKey"].get("")!.size).toBe(2); setTimeout(() => { // The batch shouldn't be fired yet, so we can add one more request. batcher.enqueueRequest({ operation: operation3 }).subscribe({}); - try { - expect(batcher["batchesByKey"].get("")!.size).toBe(3); - } catch (e) { - reject(e); - } + expect(batcher["batchesByKey"].get("")!.size).toBe(3); }, 5); - setTimeout( - terminatingCheck(resolve, reject, () => { - // The batch should've been fired by now. - expect(batcher["batchesByKey"].get("")).toBeUndefined(); - }), - 20 - ); - + const promise = wait(20); jest.runAllTimers(); + await promise; + + // The batch should've been fired by now. + expect(batcher["batchesByKey"].get("")).toBeUndefined(); }); - itAsync( - "should cancel multiple queries in queue when unsubscribing and let pass still subscribed one", - (resolve, reject) => { - const data2 = { - lastName: "Hauser", - firstName: "Evans", - }; + it("should cancel multiple queries in queue when unsubscribing and let pass still subscribed one", (done) => { + const data2 = { + lastName: "Hauser", + firstName: "Evans", + }; - const batcher = new OperationBatcher({ - batchInterval: 10, - batchHandler: () => - new Observable((observer) => { - observer.next([{ data: data2 }]); - setTimeout(observer.complete.bind(observer)); - }), - }); + const batcher = new OperationBatcher({ + batchInterval: 10, + batchHandler: () => + new Observable((observer) => { + observer.next([{ data: data2 }]); + setTimeout(observer.complete.bind(observer)); + }), + }); - const query = gql` - query { - author { - firstName - lastName - } + const query = gql` + query { + author { + firstName + lastName } - `; + } + `; + + const operation: Operation = createOperation({}, { query }); + const operation2: Operation = createOperation({}, { query }); + const operation3: Operation = createOperation({}, { query }); - const operation: Operation = createOperation({}, { query }); - const operation2: Operation = createOperation({}, { query }); - const operation3: Operation = createOperation({}, { query }); + const sub1 = batcher.enqueueRequest({ operation }).subscribe(() => { + throw new Error("next should never be called"); + }); + batcher.enqueueRequest({ operation: operation2 }).subscribe((result) => { + expect(result.data).toBe(data2); - const sub1 = batcher - .enqueueRequest({ operation }) - .subscribe(() => reject("next should never be called")); - batcher.enqueueRequest({ operation: operation2 }).subscribe((result) => { - expect(result.data).toBe(data2); + // The batch should've been fired by now. + expect(batcher["batchesByKey"].get("")).toBeUndefined(); - // The batch should've been fired by now. - expect(batcher["batchesByKey"].get("")).toBeUndefined(); + done(); + }); - resolve(); - }); + expect(batcher["batchesByKey"].get("")!.size).toBe(2); + sub1.unsubscribe(); + expect(batcher["batchesByKey"].get("")!.size).toBe(1); + + setTimeout(() => { + // The batch shouldn't be fired yet, so we can add one more request. + const sub3 = batcher + .enqueueRequest({ operation: operation3 }) + .subscribe(() => { + throw new Error("next should never be called"); + }); expect(batcher["batchesByKey"].get("")!.size).toBe(2); - sub1.unsubscribe(); + sub3.unsubscribe(); expect(batcher["batchesByKey"].get("")!.size).toBe(1); + }, 5); - setTimeout(() => { - // The batch shouldn't be fired yet, so we can add one more request. - const sub3 = batcher - .enqueueRequest({ operation: operation3 }) - .subscribe(() => reject("next should never be called")); - expect(batcher["batchesByKey"].get("")!.size).toBe(2); - - sub3.unsubscribe(); - expect(batcher["batchesByKey"].get("")!.size).toBe(1); - }, 5); + jest.runAllTimers(); + }); - jest.runAllTimers(); - } - ); - - itAsync( - "should reject the promise if there is a network error", - (resolve, reject) => { - const query = gql` - query { - author { - firstName - lastName - } + it("should reject the promise if there is a network error", async () => { + const query = gql` + query { + author { + firstName + lastName } - `; - const operation: Operation = createOperation({}, { query }); - const error = new Error("Network error"); - const BH = createMockBatchHandler({ - request: { query }, - error, - }); - const batcher = new OperationBatcher({ - batchInterval: 10, - batchHandler: BH, - }); + } + `; + const operation: Operation = createOperation({}, { query }); + const error = new Error("Network error"); + const BH = createMockBatchHandler({ + request: { query }, + error, + }); + const batcher = new OperationBatcher({ + batchInterval: 10, + batchHandler: BH, + }); - const observable = batcher.enqueueRequest({ operation }); - observable.subscribe({ - error: terminatingCheck(resolve, reject, (resError: Error) => { - expect(resError.message).toBe("Network error"); - }), - }); - batcher.consumeQueue(); - } - ); + const observable = batcher.enqueueRequest({ operation }); + const stream = new ObservableStream(observable); + batcher.consumeQueue(); + + await expect(stream).toEmitError(error); + }); }); describe("BatchLink", () => { @@ -781,25 +668,21 @@ describe("BatchLink", () => { ).not.toThrow(); }); - itAsync("passes forward on", (resolve, reject) => { + it("passes forward on", async () => { + expect.assertions(3); const link = ApolloLink.from([ new BatchLink({ batchInterval: 0, batchMax: 1, batchHandler: (operation, forward) => { - try { - expect(forward!.length).toBe(1); - expect(operation.length).toBe(1); - } catch (e) { - reject(e); - } + expect(forward!.length).toBe(1); + expect(operation.length).toBe(1); + return forward![0]!(operation[0]).map((result) => [result]); }, }), new ApolloLink((operation) => { - terminatingCheck(resolve, reject, () => { - expect(operation.query).toEqual(query); - })(); + expect(operation.query).toEqual(query); return null; }), ]); @@ -812,7 +695,7 @@ describe("BatchLink", () => { query, } ) - ).subscribe((result) => reject()); + ).subscribe(() => {}); }); it("raises warning if terminating", () => { @@ -849,28 +732,17 @@ describe("BatchLink", () => { expect(calls).toBe(2); }); - itAsync("correctly uses batch size", (resolve, reject) => { + it("correctly uses batch size", async () => { const sizes = [1, 2, 3]; const terminating = new ApolloLink((operation) => { - try { - expect(operation.query).toEqual(query); - } catch (e) { - reject(e); - } + expect(operation.query).toEqual(query); return Observable.of(operation.variables.count); }); - let runBatchSize = () => { - const size = sizes.pop(); - if (!size) resolve(); - + let runBatchSize = async (size: number) => { const batchHandler = jest.fn((operation, forward) => { - try { - expect(operation.length).toBe(size); - expect(forward.length).toBe(size); - } catch (e) { - reject(e); - } + expect(operation.length).toBe(size); + expect(forward.length).toBe(size); const observables = forward.map((f: any, i: any) => f(operation[i])); return new Observable((observer) => { const data: any[] = []; @@ -895,45 +767,43 @@ describe("BatchLink", () => { terminating, ]); - Array.from(new Array(size)).forEach((_, i) => { - execute(link, { - query, - variables: { count: i }, - }).subscribe({ - next: (data) => { - expect(data).toBe(i); - }, - complete: () => { - try { - expect(batchHandler.mock.calls.length).toBe(1); - } catch (e) { - reject(e); - } - runBatchSize(); - }, - }); - }); + return Promise.all( + Array.from(new Array(size)).map((_, i) => { + return new Promise((resolve) => { + execute(link, { + query, + variables: { count: i }, + }).subscribe({ + next: (data) => { + expect(data).toBe(i); + }, + complete: () => { + expect(batchHandler.mock.calls.length).toBe(1); + resolve(); + }, + }); + }); + }) + ); }; - runBatchSize(); + for (const size of sizes) { + await runBatchSize(size); + } }); - itAsync("correctly follows batch interval", (resolve, reject) => { + it("correctly follows batch interval", (done) => { const intervals = [10, 20, 30]; const runBatchInterval = () => { const mock = jest.fn(); const batchInterval = intervals.pop(); - if (!batchInterval) return resolve(); + if (!batchInterval) return done(); const batchHandler = jest.fn((operation, forward) => { - try { - expect(operation.length).toBe(1); - expect(forward.length).toBe(1); - } catch (e) { - reject(e); - } + expect(operation.length).toBe(1); + expect(forward.length).toBe(1); return forward[0](operation[0]).map((d: any) => [d]); }); @@ -957,11 +827,7 @@ describe("BatchLink", () => { ) ).subscribe({ next: (data) => { - try { - expect(data).toBe(42); - } catch (e) { - reject(e); - } + expect(data).toBe(42); }, complete: () => { mock(batchHandler.mock.calls.length); @@ -972,19 +838,15 @@ describe("BatchLink", () => { await delay(batchInterval); const checkCalls = mock.mock.calls.slice(0, -1); - try { - expect(checkCalls.length).toBe(2); - checkCalls.forEach((args) => expect(args[0]).toBe(0)); - expect(mock).lastCalledWith(1); - expect(batchHandler.mock.calls.length).toBe(1); - } catch (e) { - reject(e); - } + expect(checkCalls.length).toBe(2); + checkCalls.forEach((args) => expect(args[0]).toBe(0)); + expect(mock).lastCalledWith(1); + expect(batchHandler.mock.calls.length).toBe(1); runBatchInterval(); }; - delayedBatchInterval(); + void delayedBatchInterval(); mock(batchHandler.mock.calls.length); mock(batchHandler.mock.calls.length); @@ -994,97 +856,82 @@ describe("BatchLink", () => { runBatchInterval(); }); - itAsync( - "throws an error when more requests than results", - (resolve, reject) => { - const result = [{ data: {} }]; - const batchHandler = jest.fn((op) => Observable.of(result)); + it("throws an error when more requests than results", () => { + expect.assertions(4); + const result = [{ data: {} }]; + const batchHandler = jest.fn((op) => Observable.of(result)); + + const link = ApolloLink.from([ + new BatchLink({ + batchInterval: 10, + batchMax: 2, + batchHandler, + }), + ]); + + [1, 2].forEach((x) => { + execute(link, { + query, + }).subscribe({ + next: (data) => { + throw new Error("next should not be called"); + }, + error: (error: any) => { + expect(error).toBeDefined(); + expect(error.result).toEqual(result); + }, + complete: () => { + throw new Error("complete should not be called"); + }, + }); + }); + }); + + describe("batchKey", () => { + it("should allow different batches to be created separately", (done) => { + const data = { data: {} }; + const result = [data, data]; + + const batchHandler = jest.fn((op) => { + expect(op.length).toBe(2); + return Observable.of(result); + }); + let key = true; + const batchKey = () => { + key = !key; + return "" + !key; + }; const link = ApolloLink.from([ new BatchLink({ - batchInterval: 10, + batchInterval: 1, + //if batchKey does not work, then the batch size would be 3 batchMax: 2, batchHandler, + batchKey, }), ]); - [1, 2].forEach((x) => { + let count = 0; + [1, 2, 3, 4].forEach(() => { execute(link, { query, }).subscribe({ - next: (data) => { - reject("next should not be called"); + next: (d) => { + expect(d).toEqual(data); + }, + error: (e) => { + throw e; }, - error: terminatingCheck(resolve, reject, (error: any) => { - expect(error).toBeDefined(); - expect(error.result).toEqual(result); - }), complete: () => { - reject("complete should not be called"); + count++; + if (count === 4) { + expect(batchHandler.mock.calls.length).toBe(2); + done(); + } }, }); }); - } - ); - - describe("batchKey", () => { - itAsync( - "should allow different batches to be created separately", - (resolve, reject) => { - const data = { data: {} }; - const result = [data, data]; - - const batchHandler = jest.fn((op) => { - try { - expect(op.length).toBe(2); - } catch (e) { - reject(e); - } - return Observable.of(result); - }); - let key = true; - const batchKey = () => { - key = !key; - return "" + !key; - }; - - const link = ApolloLink.from([ - new BatchLink({ - batchInterval: 1, - //if batchKey does not work, then the batch size would be 3 - batchMax: 2, - batchHandler, - batchKey, - }), - ]); - - let count = 0; - [1, 2, 3, 4].forEach(() => { - execute(link, { - query, - }).subscribe({ - next: (d) => { - try { - expect(d).toEqual(data); - } catch (e) { - reject(e); - } - }, - error: reject, - complete: () => { - count++; - if (count === 4) { - try { - expect(batchHandler.mock.calls.length).toBe(2); - resolve(); - } catch (e) { - reject(e); - } - } - }, - }); - }); - } - ); + }); }); });