UNPKG

apollo-angular

Version:

Use your GraphQL data in your Angular app, with the Apollo Client

283 lines (276 loc) 11.6 kB
import * as i1 from 'apollo-angular'; import { Apollo } from 'apollo-angular'; import * as i0 from '@angular/core'; import { Injectable, InjectionToken, NgModule, Optional, Inject } from '@angular/core'; import * as i3 from '@apollo/client/core'; import { ApolloError, Observable, ApolloLink, InMemoryCache } from '@apollo/client/core'; import { Kind, OperationTypeNode, print } from 'graphql'; import { getMainDefinition } from '@apollo/client/utilities'; /** * Controller to be injected into tests, that allows for mocking and flushing * of operations. * * */ class ApolloTestingController { } const isApolloError = (err) => err && err.hasOwnProperty('graphQLErrors'); class TestOperation { operation; observer; constructor(operation, observer) { this.operation = operation; this.observer = observer; } flush(result) { if (isApolloError(result)) { this.observer.error(result); } else { const fetchResult = result ? { ...result } : result; this.observer.next(fetchResult); const definition = getMainDefinition(this.operation.query); if (definition.kind === Kind.OPERATION_DEFINITION && definition.operation !== OperationTypeNode.SUBSCRIPTION) { this.complete(); } } } complete() { this.observer.complete(); } flushData(data) { this.flush({ data, }); } networkError(error) { const apolloError = new ApolloError({ networkError: error, }); this.flush(apolloError); } graphqlErrors(errors) { this.flush({ errors, }); } } /** * A testing backend for `Apollo`. * * `ApolloTestingBackend` works by keeping a list of all open operations. * As operations come in, they're added to the list. Users can assert that specific * operations were made and then flush them. In the end, a `verify()` method asserts * that no unexpected operations were made. */ class ApolloTestingBackend { /** * List of pending operations which have not yet been expected. */ open = []; /** * Handle an incoming operation by queueing it in the list of open operations. */ handle(op) { return new Observable((observer) => { const testOp = new TestOperation(op, observer); this.open.push(testOp); }); } /** * Helper function to search for operations in the list of open operations. */ _match(match) { if (typeof match === 'string') { return this.open.filter(testOp => testOp.operation.operationName === match); } else if (typeof match === 'function') { return this.open.filter(testOp => match(testOp.operation)); } else { if (this.isDocumentNode(match)) { return this.open.filter(testOp => print(testOp.operation.query) === print(match)); } return this.open.filter(testOp => this.matchOp(match, testOp)); } } matchOp(match, testOp) { const variables = JSON.stringify(match.variables); const extensions = JSON.stringify(match.extensions); const sameName = this.compare(match.operationName, testOp.operation.operationName); const sameVariables = this.compare(variables, testOp.operation.variables); const sameQuery = print(testOp.operation.query) === print(match.query); const sameExtensions = this.compare(extensions, testOp.operation.extensions); return sameName && sameVariables && sameQuery && sameExtensions; } compare(expected, value) { const received = typeof value === 'string' ? value : JSON.stringify(value); return !expected || received === expected; } /** * Search for operations in the list of open operations, and return all that match * without asserting anything about the number of matches. */ match(match) { const results = this._match(match); results.forEach(result => { const index = this.open.indexOf(result); if (index !== -1) { this.open.splice(index, 1); } }); return results; } /** * Expect that a single outstanding request matches the given matcher, and return * it. * * operations returned through this API will no longer be in the list of open operations, * and thus will not match twice. */ expectOne(match, description) { description = description || this.descriptionFromMatcher(match); const matches = this.match(match); if (matches.length > 1) { throw new Error(`Expected one matching operation for criteria "${description}", found ${matches.length} operations.`); } if (matches.length === 0) { throw new Error(`Expected one matching operation for criteria "${description}", found none.`); } return matches[0]; } /** * Expect that no outstanding operations match the given matcher, and throw an error * if any do. */ expectNone(match, description) { description = description || this.descriptionFromMatcher(match); const matches = this.match(match); if (matches.length > 0) { throw new Error(`Expected zero matching operations for criteria "${description}", found ${matches.length}.`); } } /** * Validate that there are no outstanding operations. */ verify() { const open = this.open; if (open.length > 0) { // Show the methods and URLs of open operations in the error, for convenience. const operations = open.map(testOp => testOp.operation.operationName).join(', '); throw new Error(`Expected no open operations, found ${open.length}: ${operations}`); } } isDocumentNode(docOrOp) { return !docOrOp.operationName; } descriptionFromMatcher(matcher) { if (typeof matcher === 'string') { return `Match operationName: ${matcher}`; } else if (typeof matcher === 'object') { if (this.isDocumentNode(matcher)) { return `Match DocumentNode`; } const name = matcher.operationName || '(any)'; const variables = JSON.stringify(matcher.variables) || '(any)'; return `Match operation: ${name}, variables: ${variables}`; } else { return `Match by function: ${matcher.name}`; } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingBackend, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingBackend }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingBackend, decorators: [{ type: Injectable }] }); const APOLLO_TESTING_CACHE = new InjectionToken('apollo-angular/testing cache'); const APOLLO_TESTING_NAMED_CACHE = new InjectionToken('apollo-angular/testing named cache'); const APOLLO_TESTING_CLIENTS = new InjectionToken('apollo-angular/testing named clients'); function addClient(name, op) { op.clientName = name; return op; } class ApolloTestingModuleCore { constructor(apollo, backend, namedClients, cache, namedCaches) { function createOptions(name, c) { return { connectToDevTools: false, link: new ApolloLink(operation => backend.handle(addClient(name, operation))), cache: c || new InMemoryCache({ addTypename: false, }), }; } apollo.create(createOptions('default', cache)); if (namedClients && namedClients.length) { namedClients.forEach(name => { const caches = namedCaches && typeof namedCaches === 'object' ? namedCaches : {}; apollo.createNamed(name, createOptions(name, caches[name])); }); } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingModuleCore, deps: [{ token: i1.Apollo }, { token: ApolloTestingBackend }, { token: APOLLO_TESTING_CLIENTS, optional: true }, { token: APOLLO_TESTING_CACHE, optional: true }, { token: APOLLO_TESTING_NAMED_CACHE, optional: true }], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingModuleCore }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingModuleCore, providers: [ Apollo, ApolloTestingBackend, { provide: ApolloTestingController, useExisting: ApolloTestingBackend }, ] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingModuleCore, decorators: [{ type: NgModule, args: [{ providers: [ Apollo, ApolloTestingBackend, { provide: ApolloTestingController, useExisting: ApolloTestingBackend }, ], }] }], ctorParameters: () => [{ type: i1.Apollo }, { type: ApolloTestingBackend }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [APOLLO_TESTING_CLIENTS] }] }, { type: i3.ApolloCache, decorators: [{ type: Optional }, { type: Inject, args: [APOLLO_TESTING_CACHE] }] }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [APOLLO_TESTING_NAMED_CACHE] }] }] }); class ApolloTestingModule { static withClients(names) { return { ngModule: ApolloTestingModuleCore, providers: [ { provide: APOLLO_TESTING_CLIENTS, useValue: names, }, ], }; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingModule, imports: [ApolloTestingModuleCore] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingModule, imports: [ApolloTestingModuleCore] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.5", ngImport: i0, type: ApolloTestingModule, decorators: [{ type: NgModule, args: [{ imports: [ApolloTestingModuleCore], }] }] }); /** * Generated bundle index. Do not edit. */ export { APOLLO_TESTING_CACHE, APOLLO_TESTING_NAMED_CACHE, ApolloTestingController, ApolloTestingModule, TestOperation }; //# sourceMappingURL=ngApolloTesting.mjs.map