UNPKG

@zendesk/laika

Version:

Test, mock, intercept and modify Apollo Client's operations — in both browser and unit tests!

259 lines 13.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const graphql_tag_1 = __importDefault(require("graphql-tag")); const wait_for_observables_1 = __importDefault(require("wait-for-observables")); const core_1 = require("@apollo/client/core"); const constants_1 = require("./constants"); const laika_1 = require("./laika"); const testUtils_1 = require("./testUtils"); const query = (0, graphql_tag_1.default) ` query helloQuery { sample { id } } `; const goodbyeQuery = (0, graphql_tag_1.default) ` query goodbyeQuery { sample { id } } `; const subscription = (0, graphql_tag_1.default) ` subscription helloSubscription { sample { id } } `; const standardError = new Error('I never work'); const data = { data: { hello: 'world' } }; const mockData = { data: { goodbye: 'world' } }; const mockDataImmediate = { data: { so: 'fast' } }; describe('Laika', () => { it('returns passthrough data from the following link', () => __awaiter(void 0, void 0, void 0, function* () { const laika = new laika_1.Laika({ referenceName: constants_1.DEFAULT_GLOBAL_PROPERTY_NAME, }); const interceptionLink = laika.createLink(); const backendStub = jest.fn(() => core_1.Observable.of(data)); const link = core_1.ApolloLink.from([interceptionLink, backendStub]); const [result] = (yield (0, wait_for_observables_1.default)((0, core_1.execute)(link, { query }))); const { values } = result; expect(values).toEqual([data]); expect(backendStub).toHaveBeenCalledTimes(1); })); describe('Intercept API', () => { it('returns mocked data and does not connect to the following link', () => __awaiter(void 0, void 0, void 0, function* () { const laika = new laika_1.Laika({ referenceName: constants_1.DEFAULT_GLOBAL_PROPERTY_NAME, }); const interceptionLink = laika.createLink(); const backendStub = jest.fn(() => core_1.Observable.of(data)); const link = core_1.ApolloLink.from([interceptionLink, backendStub]); const interceptor = laika.intercept(); interceptor.mockResultOnce({ result: mockData, }); const [result] = (yield (0, wait_for_observables_1.default)((0, core_1.execute)(link, { query }))); const { values } = result; expect(values).toEqual([mockData]); expect(backendStub).toHaveBeenCalledTimes(0); })); it('returns mock once and then falls back to the following link - twice in a row', () => __awaiter(void 0, void 0, void 0, function* () { const laika = new laika_1.Laika({ referenceName: constants_1.DEFAULT_GLOBAL_PROPERTY_NAME, }); const interceptionLink = laika.createLink(); const backendStub = jest.fn(() => core_1.Observable.of(data)); const link = core_1.ApolloLink.from([interceptionLink, backendStub]); const interceptor = laika.intercept(); let triedCount = 0; while (++triedCount <= 2) { interceptor.mockResultOnce({ result: mockData, }); // eslint-disable-next-line no-await-in-loop const [result1, result2] = (yield (0, wait_for_observables_1.default)((0, core_1.execute)(link, { query }), (0, core_1.execute)(link, { query }))); const { values: mockValues } = result1; const { values: remoteValues } = result2; expect(mockValues).toEqual([mockData]); expect(remoteValues).toEqual([data]); expect(backendStub).toHaveBeenCalledTimes(triedCount); } })); it('connects to a mocked subscription without connecting to the following link and immediately fires mocked data', () => __awaiter(void 0, void 0, void 0, function* () { const laika = new laika_1.Laika({ referenceName: constants_1.DEFAULT_GLOBAL_PROPERTY_NAME, }); const interceptionLink = laika.createLink(); const mockedResultFn = jest.fn(() => ({ result: mockDataImmediate })); const backendStub = jest.fn(() => core_1.Observable.of(data)); const link = core_1.ApolloLink.from([interceptionLink, backendStub]); const interceptor = laika.intercept(); // testing that this will get pushed immediately interceptor.mockResultOnce(mockedResultFn); const observer = { next: jest.fn(), complete: jest.fn(), error: jest.fn(), }; const sub = (0, core_1.execute)(link, { query: subscription }).subscribe(observer); expect.assertions(7); yield (0, testUtils_1.onNextTick)(() => { expect(mockedResultFn).toHaveBeenCalledTimes(1); expect(observer.next).toHaveBeenCalledTimes(1); expect(observer.next).toHaveBeenCalledWith(mockDataImmediate); expect(observer.complete).not.toHaveBeenCalled(); expect(backendStub).toHaveBeenCalledTimes(0); sub.unsubscribe(); expect(observer.complete).not.toHaveBeenCalled(); expect(observer.error).not.toHaveBeenCalled(); }); })); it('connects to a mocked subscription without connecting to the following link, then fires a mock update', () => __awaiter(void 0, void 0, void 0, function* () { const laika = new laika_1.Laika({ referenceName: constants_1.DEFAULT_GLOBAL_PROPERTY_NAME, }); const interceptionLink = laika.createLink(); const backendStub = jest.fn(() => core_1.Observable.of(data)); const link = core_1.ApolloLink.from([interceptionLink, backendStub]); const interceptor = laika.intercept(); const observer = { next: jest.fn(), complete: jest.fn(), error: jest.fn(), }; expect.assertions(7); const sub = (0, core_1.execute)(link, { query: subscription }).subscribe(observer); yield (0, testUtils_1.onNextTick)(() => { expect(observer.next).not.toHaveBeenCalled(); interceptor.fireSubscriptionUpdate({ result: mockData }); expect(observer.next).toHaveBeenCalledTimes(1); expect(observer.next).toHaveBeenCalledWith(mockData); expect(observer.complete).not.toHaveBeenCalled(); expect(backendStub).toHaveBeenCalledTimes(0); sub.unsubscribe(); expect(observer.complete).not.toHaveBeenCalled(); expect(observer.error).not.toHaveBeenCalled(); }); })); it('waitForActiveSubscription generates a Promise when no current active subscription, which resolves once one is made', () => __awaiter(void 0, void 0, void 0, function* () { const laika = new laika_1.Laika({ referenceName: constants_1.DEFAULT_GLOBAL_PROPERTY_NAME, }); const interceptionLink = laika.createLink(); const backendStub = jest.fn(() => core_1.Observable.of(data)); const link = core_1.ApolloLink.from([interceptionLink, backendStub]); const interceptor = laika.intercept(); const observer = { next: jest.fn(), complete: jest.fn(), error: jest.fn(), }; expect.assertions(3); const hasSettled = jest.fn(); const waitPromise = interceptor.waitForActiveSubscription(); expect(waitPromise).toBeInstanceOf(Promise); void waitPromise.then(hasSettled); yield (0, testUtils_1.onNextTick)(() => { expect(hasSettled).not.toHaveBeenCalled(); }); const sub = (0, core_1.execute)(link, { query: subscription }).subscribe(observer); yield (0, testUtils_1.onNextTick)(() => { expect(hasSettled).toHaveBeenCalled(); sub.unsubscribe(); }); })); describe('intercept with a matcher', () => { it.each([ ['MatcherObject (operationName)', { operationName: 'goodbyeQuery' }], ['MatcherObject (variables)', { variables: { type: 'goodbye' } }], [ 'MatcherFn', (operation) => operation.operationName === 'goodbyeQuery', ], ])('correctly intercepts only operations matched by %s and leaves other alone', (_, matcher) => __awaiter(void 0, void 0, void 0, function* () { const laika = new laika_1.Laika({ referenceName: constants_1.DEFAULT_GLOBAL_PROPERTY_NAME, }); const interceptionLink = laika.createLink(); const backendStub = jest.fn(() => core_1.Observable.of(data)); const link = core_1.ApolloLink.from([interceptionLink, backendStub]); const interceptor = laika.intercept(matcher); interceptor.mockResultOnce({ result: mockData, }); const [result1, result2] = (yield (0, wait_for_observables_1.default)((0, core_1.execute)(link, { query }), (0, core_1.execute)(link, { query: goodbyeQuery, variables: { type: 'goodbye' }, }))); const { values } = result1; const { values: goodbyeValues } = result2; expect(values).toEqual([data]); expect(goodbyeValues).toEqual([mockData]); expect(backendStub).toHaveBeenCalledTimes(1); })); }); }); it('calls unsubscribe on the appropriate downstream observable', () => __awaiter(void 0, void 0, void 0, function* () { const laika = new laika_1.Laika({ referenceName: constants_1.DEFAULT_GLOBAL_PROPERTY_NAME, }); const interceptionLink = laika.createLink(); const unsubscribeStub = jest.fn(); // Hold the test hostage until we're hit let underlyingObservable; const untilSubscribed = new Promise((resolve) => { underlyingObservable = { subscribe(observer) { resolve(undefined); // Release hold on test. void Promise.resolve().then(() => { observer.next(data); observer.complete(); }); return { unsubscribe: unsubscribeStub, closed: false }; }, }; }); const backendStub = jest.fn(); backendStub.mockReturnValueOnce(underlyingObservable); const link = core_1.ApolloLink.from([interceptionLink, backendStub]); // eslint-disable-next-line @typescript-eslint/no-shadow const subscription = (0, core_1.execute)(link, { query }).subscribe({}); yield untilSubscribed; subscription.unsubscribe(); expect(unsubscribeStub).toHaveBeenCalledTimes(1); })); it('supports multiple subscribers to the same request', () => __awaiter(void 0, void 0, void 0, function* () { const laika = new laika_1.Laika({ referenceName: constants_1.DEFAULT_GLOBAL_PROPERTY_NAME, }); const interceptionLink = laika.createLink(); const stub = jest.fn(); stub.mockReturnValueOnce((0, core_1.fromError)(standardError)); stub.mockReturnValueOnce((0, core_1.fromError)(standardError)); stub.mockReturnValueOnce(core_1.Observable.of(data)); const link = core_1.ApolloLink.from([interceptionLink, stub]); const observable = (0, core_1.execute)(link, { query }); const [result1, result2, result3] = (yield (0, wait_for_observables_1.default)(observable, observable, observable)); expect(result1).toEqual({ error: standardError }); expect(result2).toEqual({ error: standardError }); expect(result3.values).toEqual([data]); expect(stub).toHaveBeenCalledTimes(3); })); }); //# sourceMappingURL=laika.test.js.map