Skip to content

Commit d5ba41c

Browse files
committed
track our own typeStack instead of relying on typeInfo._parentTypeStack
1 parent 4240841 commit d5ba41c

File tree

1 file changed

+24
-16
lines changed

1 file changed

+24
-16
lines changed

src/methods/validate-fixture-input.ts

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
1-
import { visit, DocumentNode, Kind } from "graphql";
2-
import { TypeInfo, visitWithTypeInfo, coerceInputValue } from "graphql";
31
import {
2+
coerceInputValue,
3+
DocumentNode,
4+
getNamedType,
5+
getNullableType,
6+
GraphQLCompositeType,
7+
GraphQLList,
8+
GraphQLNamedType,
9+
GraphQLSchema,
10+
isAbstractType,
411
isInputType,
512
isListType,
613
isNullableType,
7-
GraphQLSchema,
8-
GraphQLList,
9-
getNullableType,
10-
isAbstractType,
1114
isObjectType,
12-
getNamedType,
13-
GraphQLCompositeType,
15+
Kind,
16+
TypeInfo,
17+
visit,
18+
visitWithTypeInfo,
1419
} from "graphql";
1520
import { inlineNamedFragmentSpreads } from "../utils/inline-named-fragment-spreads.js";
1621

@@ -38,9 +43,11 @@ export function validateFixtureInput(
3843
const inlineFragmentSpreadsAst = inlineNamedFragmentSpreads(queryAST);
3944
const typeInfo = new TypeInfo(schema);
4045
const valueStack: any[][] = [[value]];
41-
const errors: string[] = [];
46+
const typeStack: (GraphQLNamedType | undefined)[] = [];
4247
const typenameResponseKeyStack: (string | undefined)[] = [];
4348

49+
const errors: string[] = [];
50+
4451
visit(
4552
inlineFragmentSpreadsAst,
4653
visitWithTypeInfo(typeInfo, {
@@ -54,6 +61,8 @@ export function validateFixtureInput(
5461
const fieldDefinition = typeInfo.getFieldDef();
5562
const fieldType = fieldDefinition?.type;
5663

64+
typeStack.push(getNamedType(fieldType));
65+
5766
for (const currentValue of currentValues) {
5867
const valueForResponseKey = currentValue[responseKey];
5968

@@ -67,7 +76,8 @@ export function validateFixtureInput(
6776
errors.push(`Cannot validate ${responseKey}: missing parent type information`);
6877
} else {
6978
const typenameResponseKey = typenameResponseKeyStack[typenameResponseKeyStack.length - 1];
70-
if (isValueExpectedForType(currentValue, parentType, schema, typeInfo, typenameResponseKey)) {
79+
const grandparentType = typeStack[typeStack.length - 2];
80+
if (isValueExpectedForType(currentValue, parentType, grandparentType, schema, typenameResponseKey)) {
7181
errors.push(`Missing expected fixture data for ${responseKey}`);
7282
}
7383
}
@@ -138,6 +148,7 @@ export function validateFixtureInput(
138148
},
139149
leave() {
140150
valueStack.pop();
151+
typeStack.pop();
141152
},
142153
},
143154
SelectionSet: {
@@ -254,8 +265,8 @@ function processNestedArrays(
254265
*
255266
* @param fixtureValue - The fixture value to check
256267
* @param parentType - The parent type from typeInfo
268+
* @param grandparentType - The type returned by the grandparent field (used to detect union/interface contexts)
257269
* @param schema - The GraphQL schema to resolve possible types for abstract types
258-
* @param typeInfo - TypeInfo instance to check for abstract types in ancestry
259270
* @param typenameKey - The response key for the __typename field (supports aliases like `type: __typename`)
260271
* @returns True if the value is expected for the parent type, false otherwise
261272
*
@@ -272,16 +283,13 @@ function processNestedArrays(
272283
function isValueExpectedForType(
273284
fixtureValue: any,
274285
parentType: GraphQLCompositeType,
286+
grandparentType: GraphQLNamedType | undefined,
275287
schema: GraphQLSchema,
276-
typeInfo: TypeInfo,
277288
typenameKey?: string
278289
): boolean {
279290
// If __typename wasn't selected in the query, we can't discriminate
280291
if (!typenameKey) {
281-
// Empty objects {} are valid if the parent field returns a union/interface
282-
// Check if the grandparent type (one level back in the stack) is abstract
283-
const parentStack = (typeInfo as any)._parentTypeStack;
284-
const grandparentType = parentStack[parentStack.length - 2];
292+
// Empty objects {} are valid if the grandparent type is a union/interface
285293
if (grandparentType && isAbstractType(grandparentType) && Object.keys(fixtureValue).length === 0) {
286294
return false; // Don't expect any fields on empty objects in union/interface contexts
287295
}

0 commit comments

Comments
 (0)