Skip to content

Commit ed7c548

Browse files
authored
test(execution): add additional tests from original PR #4458 (#4595)
Adds non-null sibling-error propagation tests from PR #4458 (originally from v16 work) that were not ported to next.
1 parent 79ad94b commit ed7c548

File tree

1 file changed

+183
-0
lines changed

1 file changed

+183
-0
lines changed

src/execution/__tests__/nonnull-test.ts

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ import { expect } from 'chai';
22
import { describe, it } from 'mocha';
33

44
import { expectJSON } from '../../__testUtils__/expectJSON.js';
5+
import { resolveOnNextTick } from '../../__testUtils__/resolveOnNextTick.js';
56

7+
import { invariant } from '../../jsutils/invariant.js';
68
import type { PromiseOrValue } from '../../jsutils/PromiseOrValue.js';
9+
import { promiseWithResolvers } from '../../jsutils/promiseWithResolvers.js';
710

811
import { parse } from '../../language/parser.js';
912

@@ -526,6 +529,186 @@ describe('Execute: handles non-nullable types', () => {
526529
});
527530
});
528531

532+
describe('Handles multiple errors for a single response position', () => {
533+
it('nullable and non-nullable root fields throw nested errors', async () => {
534+
const query = `
535+
{
536+
promiseNonNullNest {
537+
syncNonNull
538+
}
539+
promiseNest {
540+
syncNonNull
541+
}
542+
}
543+
`;
544+
const result = await executeQuery(query, throwingData);
545+
546+
expectJSON(result).toDeepEqual({
547+
data: null,
548+
errors: [
549+
{
550+
message: syncNonNullError.message,
551+
path: ['promiseNest', 'syncNonNull'],
552+
locations: [{ line: 7, column: 13 }],
553+
},
554+
{
555+
message: syncNonNullError.message,
556+
path: ['promiseNonNullNest', 'syncNonNull'],
557+
locations: [{ line: 4, column: 13 }],
558+
},
559+
],
560+
});
561+
});
562+
563+
it('a nullable root field throws a slower nested error after a non-nullable root field throws a nested error', async () => {
564+
const query = `
565+
{
566+
promiseNonNullNest {
567+
syncNonNull
568+
}
569+
promiseNest {
570+
promiseNonNull
571+
}
572+
}
573+
`;
574+
const result = await executeQuery(query, throwingData);
575+
576+
expectJSON(result).toDeepEqual({
577+
data: null,
578+
errors: [
579+
{
580+
message: syncNonNullError.message,
581+
path: ['promiseNonNullNest', 'syncNonNull'],
582+
locations: [{ line: 4, column: 13 }],
583+
},
584+
],
585+
});
586+
587+
// allow time for slower error to reject
588+
invariant(result.errors !== undefined);
589+
const initialErrors = [...result.errors];
590+
for (let i = 0; i < 5; i++) {
591+
// eslint-disable-next-line no-await-in-loop
592+
await resolveOnNextTick();
593+
}
594+
expectJSON(initialErrors).toDeepEqual(result.errors);
595+
});
596+
597+
it('nullable and non-nullable nested fields throw nested errors', async () => {
598+
const query = `
599+
{
600+
syncNest {
601+
promiseNonNullNest {
602+
syncNonNull
603+
}
604+
promiseNest {
605+
syncNonNull
606+
}
607+
}
608+
}
609+
`;
610+
const result = await executeQuery(query, throwingData);
611+
612+
expectJSON(result).toDeepEqual({
613+
data: { syncNest: null },
614+
errors: [
615+
{
616+
message: syncNonNullError.message,
617+
path: ['syncNest', 'promiseNest', 'syncNonNull'],
618+
locations: [{ line: 8, column: 15 }],
619+
},
620+
{
621+
message: syncNonNullError.message,
622+
path: ['syncNest', 'promiseNonNullNest', 'syncNonNull'],
623+
locations: [{ line: 5, column: 15 }],
624+
},
625+
],
626+
});
627+
});
628+
629+
it('a nullable nested field throws a slower nested error after a non-nullable nested field throws a nested error', async () => {
630+
const query = `
631+
{
632+
syncNest {
633+
promiseNonNullNest {
634+
syncNonNull
635+
}
636+
promiseNest {
637+
promiseNest {
638+
promiseNest {
639+
promiseNonNull
640+
}
641+
}
642+
}
643+
}
644+
}
645+
`;
646+
const result = await executeQuery(query, throwingData);
647+
648+
expectJSON(result).toDeepEqual({
649+
data: { syncNest: null },
650+
errors: [
651+
{
652+
message: syncNonNullError.message,
653+
path: ['syncNest', 'promiseNonNullNest', 'syncNonNull'],
654+
locations: [{ line: 5, column: 15 }],
655+
},
656+
],
657+
});
658+
659+
invariant(result.errors !== undefined);
660+
const initialErrors = [...result.errors];
661+
for (let i = 0; i < 20; i++) {
662+
// eslint-disable-next-line no-await-in-loop
663+
await resolveOnNextTick();
664+
}
665+
expectJSON(initialErrors).toDeepEqual(result.errors);
666+
});
667+
668+
it('suppresses a later error after a parent has been nulled', async () => {
669+
const query = `
670+
{
671+
syncNest {
672+
syncNonNull
673+
promise
674+
}
675+
}
676+
`;
677+
678+
const nonNullDeferred = promiseWithResolvers<unknown>();
679+
const promiseDeferred = promiseWithResolvers<unknown>();
680+
681+
const resultPromise = executeQuery(query, {
682+
syncNest: {
683+
syncNonNull: () => nonNullDeferred.promise,
684+
promise: () => promiseDeferred.promise,
685+
},
686+
});
687+
688+
nonNullDeferred.reject(syncNonNullError);
689+
690+
// Give the first error a chance to null out the parent position.
691+
await resolveOnNextTick();
692+
await resolveOnNextTick();
693+
await resolveOnNextTick();
694+
695+
promiseDeferred.reject(promiseError);
696+
697+
const result = await resultPromise;
698+
699+
expectJSON(result).toDeepEqual({
700+
data: { syncNest: null },
701+
errors: [
702+
{
703+
message: syncNonNullError.message,
704+
path: ['syncNest', 'syncNonNull'],
705+
locations: [{ line: 4, column: 13 }],
706+
},
707+
],
708+
});
709+
});
710+
});
711+
529712
describe('Handles non-null argument', () => {
530713
const schemaWithNonNullArg = new GraphQLSchema({
531714
query: new GraphQLObjectType({

0 commit comments

Comments
 (0)