UNPKG

supertest-graphql

Version:

Extends supertest to test a GraphQL endpoint

265 lines (264 loc) 9.31 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 }); exports.SuperTestExecutionStreamingResultPool = exports.SuperTestExecutionStreamingResult = exports.SuperTestExecutionNextResult = exports.LEGACY_WEBSOCKET_PROTOCOL = void 0; const graphql_1 = require("graphql"); const graphql_ws_1 = require("graphql-ws"); const subscriptions_transport_ws_1 = require("subscriptions-transport-ws"); const ws_1 = __importDefault(require("ws")); const utils_1 = require("./utils"); /** * The protocol implemented by the library `subscriptions-transport-ws` and * that is now considered legacy. */ exports.LEGACY_WEBSOCKET_PROTOCOL = "graphql-ws"; class SuperTestExecutionNextResult { constructor(pop) { this.pop = pop; this._asserts = []; } then(onfulfilled, onrejected) { return __awaiter(this, void 0, void 0, function* () { const res = yield this.pop; yield this.assert(res); if (onfulfilled) return onfulfilled(res); // @ts-expect-error no idea why return res; }); } /** * Assert that there is no errors (`.errors` field) in response returned from the GraphQL API. */ expectNoErrors() { this._asserts.push((0, utils_1.wrapAssertFn)(utils_1.asserNoError)); return this; } assert(result) { return __awaiter(this, void 0, void 0, function* () { for (const assertFn of this._asserts) { const maybeError = yield assertFn(result); if (maybeError instanceof Error) throw maybeError; } }); } } exports.SuperTestExecutionNextResult = SuperTestExecutionNextResult; class SuperTestExecutionStreamingResult { constructor(client, subscriber) { this.client = client; this.queue = new utils_1.BlockingQueue(); subscriber({ next: (res) => this.queue.push(res), complete: () => { // do something }, error: () => { // do something }, closed: false, }); } /** * Get the next result that the operation is emitting. */ next() { return new SuperTestExecutionNextResult(this.queue.pop()); } /** * Flush the pending results from the queue. */ flush() { return this.queue.flush(); } /** * Assert that no more results are pending. */ expectNoPending() { if (this.queue.length > 0) { throw new Error(`expect no pending, but got ${this.queue.length}`); } return this; } /** * Close the operation and the connection. */ close() { return __awaiter(this, void 0, void 0, function* () { yield this.client.dispose(); }); } } exports.SuperTestExecutionStreamingResult = SuperTestExecutionStreamingResult; class SuperTestExecutionStreamingResultPool { constructor() { this._subscriptions = []; } endAll() { return __awaiter(this, void 0, void 0, function* () { yield Promise.all(this._subscriptions.map((c) => c.close())); this._subscriptions = []; }); } add(sub) { this._subscriptions.push(sub); } } exports.SuperTestExecutionStreamingResultPool = SuperTestExecutionStreamingResultPool; class SuperTestWSGraphQL { constructor(_hostname, _pool) { this._hostname = _hostname; this._pool = _pool; this._path = "/graphql"; this._protocol = "graphql-transport-ws"; } /** * Send a GraphQL Query Document to the GraphQL server for execution. * @param operation - the query to execute as string or `DocumentNode` * @param variables - the variables for this query */ subscribe(operation, variables) { this.operation(operation, variables); return this; } /** * Send a GraphQL Query Document to the GraphQL server for execution. * @param query - the query to execute as string or `DocumentNode` * @param variables - the variables for this query */ query(query, variables) { return this.operation(query, variables); } /** * Send a GraphQL Query Document to the GraphQL server for execution. * @param mutation - the mutation to execute as string or `DocumentNode` * @param variables - the variables for this mutation */ mutate(mutation, variables) { return this.operation(mutation, variables); } /** * Send a GraphQL Query Document to the GraphQL server for execution. * @param operation - the operation to execute as string or `DocumentNode` * @param variables - the variables for this operation */ operation(operation, variables) { if (typeof operation !== "string") { this._operationName = (0, utils_1.getOperationName)(operation); } this._query = typeof operation === "string" ? operation : (0, graphql_1.print)(operation); this._variables = variables; return this; } /** * Set variables. * @param - variables */ variables(variables) { this._variables = variables; return this; } /** * Set the GraphQL endpoint path. * * @default "/graphql" */ path(path) { this._path = path; return this; } /** * Set the GraphQL WebSocket porotocol. * You can set the legacy protocol with the variable `LEGACY_WEBSOCKET_PROTOCOL`. */ protocol(wsProtocol) { this._protocol = wsProtocol; return this; } /** * Set connection params. */ connectionParams(params) { this._connectionParams = params; return this; } then(onfulfilled, onrejected) { var _a; return __awaiter(this, void 0, void 0, function* () { try { const url = new URL(this._path, this._hostname).toString(); const connect = this._protocol === "graphql-ws" ? connectAndAdaptLegacyClient : connectClient; const client = yield connect(url, this._connectionParams); if (!this._query) throw new Error("Missing a query"); const query = this._query; const streamingResult = new SuperTestExecutionStreamingResult(client, (observer) => client.subscribe({ query, variables: this._variables, operationName: this._operationName, }, observer)); (_a = this._pool) === null || _a === void 0 ? void 0 : _a.add(streamingResult); if (onfulfilled) return onfulfilled(streamingResult); // @ts-expect-error no idea why return streamingResult; } catch (e) { if (onrejected) return onrejected(e); throw new Error("No rejection"); } }); } } exports.default = SuperTestWSGraphQL; const connectClient = (url, connectionParams) => __awaiter(void 0, void 0, void 0, function* () { return yield new Promise((res, reject) => { const client = (0, graphql_ws_1.createClient)({ url, connectionParams, lazy: false, onNonLazyError: (error) => reject(error), webSocketImpl: ws_1.default, }); client.on("connected", () => res(client)); }); }); const connectAndAdaptLegacyClient = (url, connectionParams) => __awaiter(void 0, void 0, void 0, function* () { const legacyClient = yield new Promise((res, reject) => { const client = new subscriptions_transport_ws_1.SubscriptionClient(url, { connectionParams, connectionCallback: (error) => { if (error) { client.close(); return reject(error); } res(client); }, }, ws_1.default); }); return { dispose: () => { legacyClient.close(); }, subscribe: (args, observer) => { // @ts-expect-error not exact but fine const { unsubscribe } = legacyClient.request(args).subscribe(observer); return unsubscribe; }, }; });