Skip to content

Commit 933b6a2

Browse files
Merge pull request #453 from apollographql/mw/testing-improvements
Testing infrastructure improvements
2 parents 33b98a0 + 58a88ef commit 933b6a2

File tree

120 files changed

+1651
-4081
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

120 files changed

+1651
-4081
lines changed

.vscode/tasks.json

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "TypeScript watch",
6+
"type": "typescript",
7+
"tsconfig": "tsconfig.json",
8+
"option": "watch",
9+
"problemMatcher": ["$tsc-watch"],
10+
"group": {
11+
"kind": "build",
12+
"isDefault": true
13+
},
14+
"presentation": {
15+
"reveal": "never",
16+
"revealProblems": "onProblem",
17+
"panel": "shared",
18+
"clear": true
19+
},
20+
"runOptions": {
21+
"runOn": "folderOpen"
22+
}
23+
}
24+
]
25+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from './snapshotSerializers';
12
export * from './fixtures';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import './toCallService';
2+
import './toHaveBeenCalledBefore';
3+
import './toHaveFetched';
4+
import './toMatchAST';

gateway-js/src/__tests__/matchers/toCallService.ts renamed to federation-integration-testsuite-js/src/matchers/toCallService.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import { QueryPlan } from '@apollo/gateway';
2-
import { PlanNode } from '../../QueryPlan';
3-
import astSerializer from '../../snapshotSerializers/astSerializer';
4-
import queryPlanSerializer from '../../snapshotSerializers/queryPlanSerializer';
1+
import { QueryPlan, PlanNode } from '@apollo/query-planner';
2+
import { astSerializer, queryPlanSerializer } from '../snapshotSerializers';
53
const prettyFormat = require('pretty-format');
64

75
declare global {
86
namespace jest {
9-
interface Matchers<R, T> {
7+
interface Matchers<R> {
108
toCallService(service: string): R;
119
}
1210
}

gateway-js/src/__tests__/matchers/toHaveBeenCalledBefore.ts renamed to federation-integration-testsuite-js/src/matchers/toHaveBeenCalledBefore.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
export {};
44
declare global {
55
namespace jest {
6-
interface Matchers<R, T> {
7-
toHaveBeenCalledBefore(spy: SpyInstance): R;
6+
interface Matchers<R> {
7+
toHaveBeenCalledBefore(secondSpy: jest.SpyInstance): R;
88
}
99
}
1010
}

gateway-js/src/__tests__/matchers/toHaveFetched.ts renamed to federation-integration-testsuite-js/src/matchers/toHaveFetched.ts

+8-11
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
11
import { RequestInit, Headers } from 'apollo-server-env';
2+
type RequestInitWithJSONBody = Omit<RequestInit, 'body'> & { body?: object }
23

34
// Make this file a module
45
// See: https://github.com/microsoft/TypeScript/issues/17736
56
export {};
67
declare global {
78
namespace jest {
8-
interface Matchers<R, T> {
9-
toHaveFetched(spy: SpyInstance): R;
9+
interface Matchers<R> {
10+
toHaveFetched(requestUrl: string, requestOpts?: RequestInitWithJSONBody): R;
11+
toHaveFetchedNth(nthCall: number, requestUrl: string, requestOpts?: RequestInitWithJSONBody): R;
1012
}
1113
}
1214
}
1315

14-
function prepareHttpOptions(requestUrl: string, requestOpts: RequestInit): RequestInit {
15-
const headers = new Headers();
16+
function prepareHttpOptions(requestUrl: string, requestOpts: RequestInitWithJSONBody): RequestInit {
17+
const headers = new Headers(requestOpts.headers);
1618
headers.set('Content-Type', 'application/json');
17-
if (requestOpts.headers) {
18-
for (let name in requestOpts.headers) {
19-
headers.set(name, requestOpts.headers[name]);
20-
}
21-
}
2219

2320
const requestHttp = {
2421
method: 'POST',
@@ -37,7 +34,7 @@ function toHaveFetched(
3734
this: jest.MatcherUtils,
3835
fetch: jest.SpyInstance,
3936
requestUrl: string,
40-
requestOpts: RequestInit
37+
requestOpts: RequestInitWithJSONBody = {}
4138
): { message(): string; pass: boolean } {
4239
const httpOptions = prepareHttpOptions(requestUrl, requestOpts);
4340
let pass = false;
@@ -60,7 +57,7 @@ function toHaveFetchedNth(
6057
fetch: jest.SpyInstance,
6158
nthCall: number,
6259
requestUrl: string,
63-
requestOpts: RequestInit
60+
requestOpts: RequestInitWithJSONBody = {}
6461
): { message(): string; pass: boolean } {
6562
const httpOptions = prepareHttpOptions(requestUrl, requestOpts);
6663
let pass = false;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { ASTNode } from 'graphql';
2+
import { MatcherHintOptions } from 'jest-matcher-utils';
3+
import { diffFormatted, indentLines, printExpectedFormatted } from './utils';
4+
5+
declare global {
6+
namespace jest {
7+
interface Matchers<R> {
8+
toMatchAST(expected: ASTNode): R;
9+
}
10+
}
11+
}
12+
13+
expect.extend({
14+
toMatchAST(received: ASTNode, expected: ASTNode) {
15+
const matcherName = 'toMatchAST';
16+
const options: MatcherHintOptions = {
17+
isNot: this.isNot,
18+
promise: this.promise,
19+
};
20+
21+
const pass = this.equals(received, expected);
22+
23+
const message = pass
24+
? () =>
25+
this.utils.matcherHint(matcherName, undefined, undefined, options) +
26+
'\n\n' +
27+
`Expected AST to not equal:\n` +
28+
indentLines(printExpectedFormatted(expected))
29+
: () =>
30+
this.utils.matcherHint(matcherName, undefined, undefined, options) +
31+
'\n\n' +
32+
diffFormatted(expected, received, {
33+
aAnnotation: 'Expected',
34+
bAnnotation: 'Received',
35+
expand: this.expand ?? true,
36+
includeChangeCounts: true,
37+
});
38+
return {
39+
message,
40+
pass,
41+
};
42+
},
43+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { QueryPlan } from '@apollo/query-planner';
2+
import { MatcherHintOptions } from 'jest-matcher-utils';
3+
import { diffFormatted, indentLines, printExpectedFormatted } from './utils';
4+
5+
declare global {
6+
namespace jest {
7+
interface Matchers<R> {
8+
toMatchQueryPlan(expected: QueryPlan): R;
9+
}
10+
}
11+
}
12+
13+
expect.extend({
14+
toMatchQueryPlan(received: QueryPlan, expected: QueryPlan) {
15+
const matcherName = 'toMatchQueryPlan';
16+
const options: MatcherHintOptions = {
17+
isNot: this.isNot,
18+
promise: this.promise,
19+
};
20+
21+
const pass = this.equals(received, expected);
22+
23+
const message = pass
24+
? () =>
25+
this.utils.matcherHint(matcherName, undefined, undefined, options) +
26+
'\n\n' +
27+
`Expected query plan to not equal:\n` +
28+
indentLines(printExpectedFormatted(expected))
29+
: () =>
30+
this.utils.matcherHint(matcherName, undefined, undefined, options) +
31+
'\n\n' +
32+
diffFormatted(expected, received, {
33+
aAnnotation: 'Expected',
34+
bAnnotation: 'Received',
35+
expand: this.expand ?? true,
36+
includeChangeCounts: true,
37+
});
38+
return {
39+
message,
40+
pass,
41+
};
42+
},
43+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import diff, { DiffOptions } from 'jest-diff';
2+
import { EXPECTED_COLOR, RECEIVED_COLOR } from 'jest-matcher-utils';
3+
import prettyFormat from 'pretty-format';
4+
import {
5+
queryPlanSerializer,
6+
astSerializer,
7+
typeSerializer,
8+
} from '../snapshotSerializers';
9+
10+
const defaultFormatOptions: prettyFormat.OptionsReceived = {
11+
plugins: [queryPlanSerializer, astSerializer, typeSerializer],
12+
};
13+
14+
export function diffFormatted(
15+
expected: unknown,
16+
received: unknown,
17+
diffOptions?: DiffOptions,
18+
formatOptions: prettyFormat.OptionsReceived = defaultFormatOptions,
19+
) {
20+
const expectedString = prettyFormat(expected, formatOptions);
21+
const receivedString = prettyFormat(received, formatOptions);
22+
23+
return diff(expectedString, receivedString, diffOptions);
24+
}
25+
26+
export function indentLines(
27+
text: string,
28+
depth: number = 1,
29+
indent: string = ' ',
30+
) {
31+
const indentation = indent.repeat(depth);
32+
return text
33+
.split('\n')
34+
.map((line) => indentation + line)
35+
.join('\n');
36+
}
37+
38+
// The corresponding functions in `jest-matcher-utils` call their own `stringify` function,
39+
// and that doesn't allow passing in custom pretty-format plugins.
40+
41+
export function printReceivedFormatted(
42+
value: unknown,
43+
formatOptions: prettyFormat.OptionsReceived = defaultFormatOptions,
44+
): string {
45+
return RECEIVED_COLOR(prettyFormat(value, formatOptions));
46+
}
47+
48+
export function printExpectedFormatted(
49+
value: unknown,
50+
formatOptions: prettyFormat.OptionsReceived = defaultFormatOptions,
51+
): string {
52+
return EXPECTED_COLOR(prettyFormat(value, formatOptions));
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export { astSerializer } from '@apollo/query-planner';
2+
export { queryPlanSerializer } from '@apollo/query-planner';
3+
export { default as selectionSetSerializer } from './selectionSetSerializer';
4+
export { default as typeSerializer } from './typeSerializer';
5+
export { default as graphqlErrorSerializer } from './graphqlErrorSerializer';
6+

federation-integration-testsuite-js/tsconfig.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
"compilerOptions": {
44
"rootDir": "./src",
55
"outDir": "./dist",
6-
"noImplicitAny": false,
7-
"strictNullChecks": false,
86
"types": ["node", "jest"]
97
},
108
"include": ["src/**/*"],
119
"exclude": ["**/__tests__"],
12-
"references": []
10+
"references": [
11+
{ "path": "../gateway-js" }
12+
]
1313
}

federation-js/jest.config.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
const config = require('../jest.config.base');
1+
const baseConfig = require('../jest.config.base');
22

3-
const additionalConfig = {};
4-
5-
module.exports = Object.assign(Object.create(null), config, additionalConfig);
3+
/** @typedef {import('ts-jest/dist/types')} */
4+
/** @type {import('@jest/types').Config.InitialOptions} */
5+
module.exports = {
6+
...baseConfig,
7+
setupFilesAfterEnv: ['./src/__tests__/testSetup.ts'],
8+
};
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import 'apollo-federation-integration-testsuite/dist/matchers';
+3-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
2-
"extends": "../../../tsconfig.test.base",
2+
"extends": "../../tsconfig.test",
33
"include": ["**/*"],
44
"references": [
5-
{ "path": "../../" },
5+
{ "path": "../.." },
6+
{ "path": "../../../federation-integration-testsuite-js" },
67
]
78
}

0 commit comments

Comments
 (0)