All files / src test-links.ts

92.31% Statements 84/91
87.5% Branches 42/48
77.27% Functions 17/22
94.52% Lines 69/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 16323x                 23x 23x                                       23x 175x 175x   175x 175x 175x 175x 169x 254x       23x 254x 254x 254x 202x 202x   254x     23x 220x 220x 220x 5x             215x 215x   215x 15x 15x   215x       215x 215x 198x 16x   182x 182x       215x 214x       23x   23x   15x 15x         15x     23x 17x 17x 17x 17x 17x         27x 27x 26x 26x 26x 26x       23x       23x     23x       474x   474x         474x           182x   129x 129x   129x 129x 129x     129x     23x      
import {
  Operation,
  GraphQLRequest,
  ApolloLink,
  FetchResult,
  Observable,
  // Observer,
} from 'apollo-link';
 
import { print } from 'graphql/language/printer';
import { addTypenameToDocument } from 'apollo-utilities';
 
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: MockedResponse[], IaddTypename: Boolean = true) {
    super();
    this.addTypename = addTypename;
    if (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);
    const responses = this.mockedResponsesByKey[key];
    if (!responses || responses.length === 0) {
      throw new Error(
        `No more mocked responses for the query: ${print(
          operation.query,
        )}, variables: ${JSON.stringify(operation.variables)}`,
      );
    }
 
    const original = [...this.mockedResponsesByKey[key]];
    const { result, error, delay, newData } = this.mockedResponsesByKey[key].shift() || ({} as any);
 
    if (newData) {
      original[0].result = newData();
      this.mockedResponsesByKey[key].push(original[0]);
    }
    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 = {
    variables: request.variables || {},
    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();
}