Skip to content

Commit

Permalink
Show NaN in MockLink debug messages for unmatched variables (#12404)
Browse files Browse the repository at this point in the history
  • Loading branch information
jerelmiller authored Mar 6, 2025
1 parent 644bb26 commit 4332b88
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 5 deletions.
2 changes: 1 addition & 1 deletion .api-reports/api-report-utilities.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ export class DeepMerger<TContextArgs extends any[]> {
//
// @public (undocumented)
export type DeepOmit<T, K> = T extends DeepOmitPrimitive ? T : {
[P in Exclude<keyof T, K>]: T[P] extends infer TP ? TP extends DeepOmitPrimitive ? TP : TP extends any[] ? DeepOmitArray<TP, K> : DeepOmit<TP, K> : never;
[P in keyof T as P extends K ? never : P]: T[P] extends infer TP ? TP extends DeepOmitPrimitive ? TP : TP extends any[] ? DeepOmitArray<TP, K> : DeepOmit<TP, K> : never;
};

// @public (undocumented)
Expand Down
5 changes: 5 additions & 0 deletions .changeset/rich-adults-destroy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@apollo/client": patch
---

Show `NaN` rather than converting to `null` in debug messages from `MockLink` for unmatched `variables` values.
14 changes: 14 additions & 0 deletions src/testing/core/mocking/__tests__/__snapshots__/mockLink.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`shows undefined and NaN in debug messages 1`] = `
"No more mocked responses for the query: query ($id: ID!, $filter: Boolean) {
usersByTestId(id: $id, filter: $filter) {
id
}
}
Expected variables: {\\"id\\":NaN,\\"filter\\":<undefined>}
Failed to match 1 mock for this query. The mocked response had the following variables:
{\\"id\\":1,\\"filter\\":true}
"
`;
34 changes: 33 additions & 1 deletion src/testing/core/mocking/__tests__/mockLink.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import gql from "graphql-tag";
import { MockLink, MockedResponse } from "../mockLink";
import { execute } from "../../../../link/core/execute";
import { ObservableStream, enableFakeTimers } from "../../../internal";
import {
ObservableStream,
enableFakeTimers,
spyOnConsole,
} from "../../../internal";

describe("MockedResponse.newData", () => {
const setup = () => {
Expand Down Expand Up @@ -357,6 +361,34 @@ test("removes fields with @client directives", async () => {
}
});

test("shows undefined and NaN in debug messages", async () => {
using _ = spyOnConsole("warn");

const query = gql`
query ($id: ID!, $filter: Boolean) {
usersByTestId(id: $id, filter: $filter) {
id
}
}
`;

const link = new MockLink([
{
request: { query, variables: { id: 1, filter: true } },
// The actual response data makes no difference in this test
result: { data: { usersByTestId: null } },
},
]);

const stream = new ObservableStream(
execute(link, { query, variables: { id: NaN, filter: undefined } })
);

const error = await stream.takeError();

expect(error.message).toMatchSnapshot();
});

describe.skip("type tests", () => {
const ANY = {} as any;
test("covariant behaviour: `MockedResponses<X,Y>` should be assignable to `MockedResponse`", () => {
Expand Down
33 changes: 30 additions & 3 deletions src/testing/core/mocking/mockLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import {
addTypenameToDocument,
removeClientSetsFromDocument,
cloneDeep,
stringifyForDisplay,
print,
getOperationDefinition,
getDefaultValues,
removeDirectivesFromDocument,
checkDocument,
makeUniqueId,
} from "../../../utilities/index.js";
import type { Unmasked } from "../../../masking/index.js";

Expand Down Expand Up @@ -136,14 +136,14 @@ export class MockLink extends ApolloLink {
if (!response) {
configError = new Error(
`No more mocked responses for the query: ${print(operation.query)}
Expected variables: ${stringifyForDisplay(operation.variables)}
Expected variables: ${stringifyForDebugging(operation.variables)}
${
unmatchedVars.length > 0 ?
`
Failed to match ${unmatchedVars.length} mock${
unmatchedVars.length === 1 ? "" : "s"
} for this query. The mocked response had the following variables:
${unmatchedVars.map((d) => ` ${stringifyForDisplay(d)}`).join("\n")}
${unmatchedVars.map((d) => ` ${stringifyForDebugging(d)}`).join("\n")}
`
: ""
}`
Expand Down Expand Up @@ -280,3 +280,30 @@ export function mockSingleLink(...mockedResponses: Array<any>): MockApolloLink {

return new MockLink(mocks, maybeTypename);
}

// This is similiar to the stringifyForDisplay utility we ship, but includes
// support for NaN in addition to undefined. More values may be handled in the
// future. This is not added to the primary stringifyForDisplay helper since it
// is used for the cache and other purposes. We need this for debuggging only.
export function stringifyForDebugging(value: any, space = 0): string {
const undefId = makeUniqueId("undefined");
const nanId = makeUniqueId("NaN");

return JSON.stringify(
value,
(_, value) => {
if (value === void 0) {
return undefId;
}

if (Number.isNaN(value)) {
return nanId;
}

return value;
},
space
)
.replace(new RegExp(JSON.stringify(undefId), "g"), "<undefined>")
.replace(new RegExp(JSON.stringify(nanId), "g"), "NaN");
}

0 comments on commit 4332b88

Please sign in to comment.