diff --git a/package-lock.json b/package-lock.json index 75937bacc9..3d779dd428 100644 --- a/package-lock.json +++ b/package-lock.json @@ -640,7 +640,6 @@ "version": "24.5.0", "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.5.0.tgz", "integrity": "sha512-kN7RFzNMf2R8UDadPOl6ReyI+MT8xfqRuAnuVL+i4gwjv/zubdDK+EDeLHYwq1j0CSSR2W/MmgaRlMZJzXdmVA==", - "dev": true, "requires": { "@types/istanbul-lib-coverage": "^1.1.0", "@types/yargs": "^12.0.9" @@ -790,8 +789,7 @@ "@types/istanbul-lib-coverage": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz", - "integrity": "sha512-ohkhb9LehJy+PA40rDtGAji61NCgdtKLAlFoYp4cnuuQEswwdK3vz9SOIkkyc3wrk8dzjphQApNs56yyXLStaQ==", - "dev": true + "integrity": "sha512-ohkhb9LehJy+PA40rDtGAji61NCgdtKLAlFoYp4cnuuQEswwdK3vz9SOIkkyc3wrk8dzjphQApNs56yyXLStaQ==" }, "@types/jest": { "version": "24.0.11", @@ -887,8 +885,7 @@ "@types/yargs": { "version": "12.0.9", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-12.0.9.tgz", - "integrity": "sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==", - "dev": true + "integrity": "sha512-sCZy4SxP9rN2w30Hlmg5dtdRwgYQfYRiLo9usw8X9cxlf+H4FqM1xX7+sNH7NNKVdbXMJWqva7iyy+fxh/V7fA==" }, "@types/zen-observable": { "version": "0.8.0", @@ -2650,7 +2647,6 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -2658,8 +2654,7 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "colors": { "version": "1.3.3", @@ -3105,8 +3100,7 @@ "diff-sequences": { "version": "24.3.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.3.0.tgz", - "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==", - "dev": true + "integrity": "sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==" }, "discontinuous-range": { "version": "1.0.0", @@ -3331,8 +3325,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { "version": "1.11.1", @@ -4089,8 +4082,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbols": { "version": "1.0.0", @@ -5139,7 +5131,6 @@ "version": "24.5.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.5.0.tgz", "integrity": "sha512-mCILZd9r7zqL9Uh6yNoXjwGQx0/J43OD2vvWVKwOEOLZliQOsojXwqboubAQ+Tszrb6DHGmNU7m4whGeB9YOqw==", - "dev": true, "requires": { "chalk": "^2.0.1", "diff-sequences": "^24.3.0", @@ -5151,7 +5142,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } @@ -5160,7 +5150,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "requires": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -5171,7 +5160,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "^3.0.0" } @@ -5303,8 +5291,7 @@ "jest-get-type": { "version": "24.3.0", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.3.0.tgz", - "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==", - "dev": true + "integrity": "sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==" }, "jest-haste-map": { "version": "24.5.0", @@ -7030,8 +7017,7 @@ "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "lodash.escape": { "version": "4.0.1", @@ -7072,7 +7058,8 @@ "lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=", + "dev": true }, "lodash.isinteger": { "version": "4.0.4", @@ -8250,7 +8237,6 @@ "version": "24.5.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.5.0.tgz", "integrity": "sha512-/3RuSghukCf8Riu5Ncve0iI+BzVkbRU5EeUoArKARZobREycuH5O4waxvaNIloEXdb0qwgmEAed5vTpX1HNROQ==", - "dev": true, "requires": { "@jest/types": "^24.5.0", "ansi-regex": "^4.0.0", @@ -8261,14 +8247,12 @@ "ansi-regex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "^1.9.0" } diff --git a/package.json b/package.json index cdd132cda5..e336fe5206 100644 --- a/package.json +++ b/package.json @@ -166,7 +166,8 @@ "dependencies": { "apollo-utilities": "^1.2.1", "hoist-non-react-statics": "^3.0.0", - "lodash.isequal": "^4.5.0", + "jest-diff": "^24.5.0", + "lodash": "^4.17.11", "prop-types": "^15.6.0", "ts-invariant": "^0.3.0", "tslib": "^1.9.3" diff --git a/src/test-links.ts b/src/test-links.ts index d7e48b2b94..51f01e5b18 100644 --- a/src/test-links.ts +++ b/src/test-links.ts @@ -8,13 +8,16 @@ import { } from 'apollo-link'; import { print } from 'graphql/language/printer'; +import { getOperationAST } from 'graphql'; import { addTypenameToDocument, removeClientSetsFromDocument, removeConnectionDirectiveFromDocument, cloneDeep, } from 'apollo-utilities'; -const isEqual = require('lodash.isequal'); +import values from 'lodash/values'; +import isEqual from 'lodash/isEqual'; +import diff from 'jest-diff'; type ResultFunction = () => T; @@ -75,10 +78,18 @@ export class MockLink extends ApolloLink { }); if (!response || typeof responseIndex === 'undefined') { + const queryDiffs = ( []).concat( + ...values(this.mockedResponsesByKey).map(mockedResponses => + mockedResponses.map(mockedResponse => + diffRequest(mockedResponse.request, operation, this.addTypename), + ), + ), + ); + throw new Error( - `No more mocked responses for the query: ${print( - operation.query, - )}, variables: ${JSON.stringify(operation.variables)}`, + `No more mocked responses for\n${requestToString(operation)}${ + queryDiffs.length ? `\n\nPossible matches:\n${queryDiffs.join('\n')}` : '' + }`, ); } @@ -178,6 +189,32 @@ function requestToKey(request: GraphQLRequest, addTypename: Boolean): string { return JSON.stringify(requestKey); } +function diffRequest( + actualRequest: GraphQLRequest, + expectedRequest: GraphQLRequest, + addTypename?: Boolean +): string { + return diff( + requestToString(actualRequest, addTypename), + requestToString(expectedRequest) + ) || ''; +} + +function requestToString( + request: GraphQLRequest, + addTypename?: Boolean +): string { + const query = print( + addTypename ? addTypenameToDocument(request.query) : request.query + ); + const variables = request.variables + ? JSON.stringify(request.variables, null, 2) + : '{}'; + const operationAST = getOperationAST(request.query, null); + const operationName = operationAST ? operationAST.operation : 'query'; + return `${operationName}:\n${query}variables:\n${variables}`; +} + // 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 diff --git a/test/__snapshots__/test-utils.test.tsx.snap b/test/__snapshots__/test-utils.test.tsx.snap index eb6975a83c..9f63fc8d4c 100644 --- a/test/__snapshots__/test-utils.test.tsx.snap +++ b/test/__snapshots__/test-utils.test.tsx.snap @@ -15,33 +15,103 @@ Object { `; exports[`errors if the query in the mock and component do not match 1`] = ` -[Error: Network error: No more mocked responses for the query: query GetUser($username: String!) { +[Error: Network error: No more mocked responses for +query: +query GetUser($username: String!) { user(username: $username) { id __typename } } -, variables: {"username":"mock_username"}] +variables: +{ + "username": "mock_username" +} + +Possible matches: +- Expected ++ Received + + query: +- query OtherQuery { +- otherQuery { ++ query GetUser($username: String!) { ++ user(username: $username) { + id + __typename + } + } + variables: + { + "username": "mock_username" + }] `; exports[`errors if the variables do not deep equal 1`] = ` -[Error: Network error: No more mocked responses for the query: query GetUser($username: String!) { +[Error: Network error: No more mocked responses for +query: +query GetUser($username: String!) { user(username: $username) { id __typename } } -, variables: {"username":"some_user","age":42}] +variables: +{ + "username": "some_user", + "age": 42 +} + +Possible matches: +- Expected ++ Received + + query: + query GetUser($username: String!) { + user(username: $username) { + id + __typename + } + } + variables: + { +- "age": 13, +- "username": "some_user" ++ "username": "some_user", ++ "age": 42 + }] `; exports[`errors if the variables in the mock and component do not match 1`] = ` -[Error: Network error: No more mocked responses for the query: query GetUser($username: String!) { +[Error: Network error: No more mocked responses for +query: +query GetUser($username: String!) { user(username: $username) { id __typename } } -, variables: {"username":"other_user"}] +variables: +{ + "username": "other_user" +} + +Possible matches: +- Expected ++ Received + + query: + query GetUser($username: String!) { + user(username: $username) { + id + __typename + } + } + variables: + { +- "username": "mock_username" ++ "username": "other_user" + }] `; exports[`mocks the data 1`] = `