Skip to content

Commit

Permalink
feat(graphql): add more tests for middleware and context resolver
Browse files Browse the repository at this point in the history
Signed-off-by: Raymond Feng <[email protected]>
  • Loading branch information
raymondfeng committed Sep 2, 2020
1 parent 82594eb commit 8db9e08
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright IBM Corp. 2020. All Rights Reserved.
// Node module: @loopback/example-graphql
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createBindingFromClass} from '@loopback/core';
import {GraphQLBindings, GraphQLServer} from '@loopback/graphql';
import {expect, supertest} from '@loopback/testlab';
import {RecipesDataSource} from '../../datasources';
import {RecipeResolver} from '../../graphql-resolvers/recipe-resolver';
import {RecipeRepository} from '../../repositories';
import {sampleRecipes} from '../../sample-recipes';
import {RecipeService} from '../../services/recipe.service';
import {exampleQuery} from './graphql-tests';

describe('GraphQL context', () => {
let server: GraphQLServer;
let repo: RecipeRepository;

before(givenServer);
after(stopServer);

it('invokes middleware', async () => {
await supertest(server.httpServer?.url)
.post('/graphql')
.set('content-type', 'application/json')
.accept('application/json')
.send({operationName: 'GetRecipe1', variables: {}, query: exampleQuery})
.expect(200);
});

async function givenServer() {
server = new GraphQLServer({host: '127.0.0.1', port: 0});
server.resolver(RecipeResolver);

// Customize the GraphQL context with additional information for test verification
server.bind(GraphQLBindings.GRAPHQL_CONTEXT_RESOLVER).to(ctx => {
return {...ctx, meta: 'loopback'};
});

// Register a GraphQL middleware to verify context resolution
server.middleware((resolverData, next) => {
expect(resolverData.context).to.containEql({meta: 'loopback'});
return next();
});

server.bind('recipes').to([...sampleRecipes]);
const repoBinding = createBindingFromClass(RecipeRepository);
server.add(repoBinding);
server.add(createBindingFromClass(RecipesDataSource));
server.add(createBindingFromClass(RecipeService));
await server.start();
repo = await server.get<RecipeRepository>(repoBinding.key);
await repo.start();
}

async function stopServer() {
if (!server) return;
await server.stop();
repo.stop();
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,71 @@
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

import {createRestAppClient, givenHttpServerConfig} from '@loopback/testlab';
import {GraphqlDemoApplication} from '../../';
import {runTests} from './graphql-tests';
import {createBindingFromClass} from '@loopback/core';
import {GraphQLServer} from '@loopback/graphql';
import {expect, supertest} from '@loopback/testlab';
import {RecipesDataSource} from '../../datasources';
import {RecipeResolver} from '../../graphql-resolvers/recipe-resolver';
import {RecipeRepository} from '../../repositories';
import {sampleRecipes} from '../../sample-recipes';
import {RecipeService} from '../../services/recipe.service';
import {exampleQuery} from './graphql-tests';

describe('GraphQL middleware', () => {
let app: GraphqlDemoApplication;
let server: GraphQLServer;
let repo: RecipeRepository;

before(giveAppWithGraphQLMiddleware);
after(stopApp);
const fieldNamesCapturedByMiddleware: string[] = [];

runTests(() => createRestAppClient(app));
before(givenServer);
after(stopServer);

async function giveAppWithGraphQLMiddleware() {
app = new GraphqlDemoApplication({rest: givenHttpServerConfig()});
await app.boot();
await app.start();
return app;
it('invokes middleware', async () => {
await supertest(server.httpServer?.url)
.post('/graphql')
.set('content-type', 'application/json')
.accept('application/json')
.send({operationName: 'GetRecipe1', variables: {}, query: exampleQuery})
.expect(200);
expect(fieldNamesCapturedByMiddleware).to.eql([
// the query
'recipe',
// field resolvers
'title',
'description',
'ratings',
'creationDate',
'ratingsCount',
'averageRating',
'ingredients',
'numberInCollection',
]);
});

async function givenServer() {
server = new GraphQLServer({host: '127.0.0.1', port: 0});
server.resolver(RecipeResolver);

// Register a GraphQL middleware
server.middleware((resolverData, next) => {
// It's invoked for each field resolver
fieldNamesCapturedByMiddleware.push(resolverData.info.fieldName);
return next();
});

server.bind('recipes').to([...sampleRecipes]);
const repoBinding = createBindingFromClass(RecipeRepository);
server.add(repoBinding);
server.add(createBindingFromClass(RecipesDataSource));
server.add(createBindingFromClass(RecipeService));
await server.start();
repo = await server.get<RecipeRepository>(repoBinding.key);
await repo.start();
}

async function stopApp() {
await app?.stop();
async function stopServer() {
if (!server) return;
await server.stop();
repo.stop();
}
});
8 changes: 4 additions & 4 deletions examples/graphql/src/__tests__/acceptance/graphql-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function runTests(getClient: () => supertest.SuperTest<supertest.Test>) {
.post('/graphql')
.set('content-type', 'application/json')
.accept('application/json')
.send({operationName: 'GetRecipe1', variables: {}, query: example})
.send({operationName: 'GetRecipe1', variables: {}, query: exampleQuery})
.expect(200, expectedData);
});

Expand All @@ -34,7 +34,7 @@ export function runTests(getClient: () => supertest.SuperTest<supertest.Test>) {
.post('/graphql')
.set('content-type', 'application/json')
.accept('application/json')
.send({operationName: 'AddRecipe', variables: {}, query: example})
.send({operationName: 'AddRecipe', variables: {}, query: exampleQuery})
.expect(200);
expect(res.body.data.addRecipe).to.containEql({
id: '4',
Expand Down Expand Up @@ -81,13 +81,13 @@ export function runTests(getClient: () => supertest.SuperTest<supertest.Test>) {
.post('/graphql')
.set('content-type', 'application/json')
.accept('application/json')
.send({operationName: 'GetRecipes', variables: {}, query: example})
.send({operationName: 'GetRecipes', variables: {}, query: exampleQuery})
.expect(200);
expect(res.body.data.recipes).to.eql(expectedRecipes);
});
}

const example = `query GetRecipe1 {
export const exampleQuery = `query GetRecipe1 {
recipe(recipeId: "1") {
title
description
Expand Down
27 changes: 26 additions & 1 deletion examples/graphql/src/__tests__/acceptance/graphql.acceptance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@

import {Application, createBindingFromClass} from '@loopback/core';
import {GraphQLServer} from '@loopback/graphql';
import {supertest} from '@loopback/testlab';
import {
createRestAppClient,
givenHttpServerConfig,
supertest,
} from '@loopback/testlab';
import {GraphqlDemoApplication} from '../../';
import {RecipesDataSource} from '../../datasources';
import {RecipeResolver} from '../../graphql-resolvers/recipe-resolver';
import {RecipeRepository} from '../../repositories';
Expand Down Expand Up @@ -72,3 +77,23 @@ describe('GraphQL application', () => {
await app.stop();
}
});

describe('GraphQL as middleware', () => {
let app: GraphqlDemoApplication;

before(giveAppWithGraphQLMiddleware);
after(stopApp);

runTests(() => createRestAppClient(app));

async function giveAppWithGraphQLMiddleware() {
app = new GraphqlDemoApplication({rest: givenHttpServerConfig()});
await app.boot();
await app.start();
return app;
}

async function stopApp() {
await app?.stop();
}
});

0 comments on commit 8db9e08

Please sign in to comment.