All files / src test-links.ts

92.78% Statements 90/97
86.54% Branches 45/52
78.26% Functions 18/23
95% Lines 76/80
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 17323x                 23x 23x 23x                                       23x 198x 198x   198x 198x 198x 198x 198x 286x       23x 286x 286x 286x 205x 205x   286x     23x 253x   253x 251x 251x 251x 4x   247x 247x     253x 6x             247x   247x   247x 19x 19x     247x       247x 247x 232x 20x   212x 212x       247x 246x       23x   23x   15x 15x         15x     23x 18x 18x 18x 18x 18x         31x 31x 30x 30x 30x 30x       23x       23x     23x       539x   539x   539x           194x   137x 137x   137x 137x 137x     137x     23x      
import {
  Operation,
  GraphQLRequest,
  ApolloLink,
  FetchResult,
  Observable,
  // Observer,
} from 'apollo-link';
 
import { print } from 'graphql/language/printer';
import { addTypenameToDocument } from 'apollo-utilities';
const isEqual = require('lodash.isequal');
 
export interface MockedResponse {
  request: GraphQLRequest;
  result?: FetchResult;
  error?: Error;
  delay?: number;
  newData?: () => FetchResult;
}
 
export interface MockedSubscriptionResult {
  result?: FetchResult;
  error?: Error;
  delay?: number;
}
 
export interface MockedSubscription {
  request: GraphQLRequest;
}
 
export class MockLink extends ApolloLink {
  public addTypename: Boolean = true;
  private mockedResponsesByKey: { [key: string]: MockedResponse[] } = {};
 
  constructor(mockedResponses: ReadonlyArray<MockedResponse>, IaddTypename: Boolean = true) {
    super();
    this.addTypename = addTypename;
    Eif (mockedResponses)
      mockedResponses.forEach(mockedResponse => {
        this.addMockedResponse(mockedResponse);
      });
  }
 
  public addMockedResponse(mockedResponse: MockedResponse) {
    const key = requestToKey(mockedResponse.request, this.addTypename);
    let mockedResponses = this.mockedResponsesByKey[key];
    if (!mockedResponses) {
      mockedResponses = [];
      this.mockedResponsesByKey[key] = mockedResponses;
    }
    mockedResponses.push(mockedResponse);
  }
 
  public request(operation: Operation) {
    const key = requestToKey(operation, this.addTypename);
    let responseIndex;
    const response = (this.mockedResponsesByKey[key] || []).find((res, index) => {
      const requestVariables = operation.variables || {};
      const mockedResponseVariables = res.request.variables || {};
      if (!isEqual(requestVariables, mockedResponseVariables)) {
        return false;
      }
      responseIndex = index;
      return true;
    });
 
    if (!response || typeof responseIndex === 'undefined') {
      throw new Error(
        `No more mocked responses for the query: ${print(
          operation.query,
        )}, variables: ${JSON.stringify(operation.variables)}`,
      );
    }
 
    this.mockedResponsesByKey[key].splice(responseIndex, 1);
 
    const { result, error, delay, newData } = response;
 
    if (newData) {
      response.result = newData();
      this.mockedResponsesByKey[key].push(response);
    }
 
    Iif (!result && !error) {
      throw new Error(`Mocked response should contain either result or error: ${key}`);
    }
 
    return new Observable<FetchResult>(observer => {
      let timer = setTimeout(() => {
        if (error) {
          observer.error(error);
        } else {
          Eif (result) observer.next(result);
          observer.complete();
        }
      }, delay ? delay : 0);
 
      return () => {
        clearTimeout(timer);
      };
    });
  }
}
 
export class MockSubscriptionLink extends ApolloLink {
  // private observer: Observer<any>;
  public unsubscribers: any[] = [];
  public setups: any[] = [];
 
  private observer: any;
 
  constructor() {
    super();
  }
 
  public request(_req: any) {
    return new Observable<FetchResult>(observer => {
      this.setups.forEach(x => x());
      this.observer = observer;
      return () => {
        this.unsubscribers.forEach(x => x());
      };
    });
  }
 
  public simulateResult(result: MockedSubscriptionResult) {
    setTimeout(() => {
      const { observer } = this;
      Iif (!observer) throw new Error('subscription torn down');
      if (result.result && observer.next) observer.next(result.result);
      if (result.error && observer.error) observer.error(result.error);
    }, result.delay || 0);
  }
 
  public onSetup(listener: any): void {
    this.setups = this.setups.concat([listener]);
  }
 
  public onUnsubscribe(listener: any): void {
    this.unsubscribers = this.unsubscribers.concat([listener]);
  }
}
 
function requestToKey(request: GraphQLRequest, addTypename: Boolean): string {
  const queryString =
    request.query && print(addTypename ? addTypenameToDocument(request.query) : request.query);
 
  const requestKey = { query: queryString };
 
  return JSON.stringify(requestKey);
}
 
// Pass in multiple mocked responses, so that you can test flows that end up
// making multiple queries to the server
// NOTE: The last arg can optionally be an `addTypename` arg
export function mockSingleLink(...mockedResponses: Array<any>): ApolloLink {
  // to pull off the potential typename. If this isn't a boolean, we'll just set it true later
  let maybeTypename = mockedResponses[mockedResponses.length - 1];
  let mocks = mockedResponses.slice(0, mockedResponses.length - 1);
 
  Eif (typeof maybeTypename !== 'boolean') {
    mocks = mockedResponses;
    maybeTypename = true;
  }
 
  return new MockLink(mocks, maybeTypename);
}
 
export function mockObservableLink(): MockSubscriptionLink {
  return new MockSubscriptionLink();
}