@@ -8,12 +8,12 @@ import { parse } from '../../language/parser.js';
88
99import { buildSchema } from '../../utilities/buildASTSchema.js' ;
1010
11- import { execute } from '../execute.js' ;
11+ import { execute , subscribe } from '../execute.js' ;
1212
1313const schema = buildSchema ( `
1414 type Todo {
1515 id: ID
16- text: String
16+ items: [ String]
1717 author: User
1818 }
1919
@@ -24,12 +24,17 @@ const schema = buildSchema(`
2424
2525 type Query {
2626 todo: Todo
27+ nonNullableTodo: Todo!
2728 }
2829
2930 type Mutation {
3031 foo: String
3132 bar: String
3233 }
34+
35+ type Subscription {
36+ foo: String
37+ }
3338` ) ;
3439
3540describe ( 'Execute: Cancellation' , ( ) => {
@@ -54,7 +59,6 @@ describe('Execute: Cancellation', () => {
5459 todo : async ( ) =>
5560 Promise . resolve ( {
5661 id : '1' ,
57- text : 'Hello, World!' ,
5862 /* c8 ignore next */
5963 author : ( ) => expect . fail ( 'Should not be called' ) ,
6064 } ) ,
@@ -149,7 +153,6 @@ describe('Execute: Cancellation', () => {
149153 todo : async ( ) =>
150154 Promise . resolve ( {
151155 id : '1' ,
152- text : 'Hello, World!' ,
153156 /* c8 ignore next */
154157 author : ( ) => expect . fail ( 'Should not be called' ) ,
155158 } ) ,
@@ -198,7 +201,6 @@ describe('Execute: Cancellation', () => {
198201 todo : async ( ) =>
199202 Promise . resolve ( {
200203 id : '1' ,
201- text : 'Hello, World!' ,
202204 /* c8 ignore next */
203205 author : ( ) => expect . fail ( 'Should not be called' ) ,
204206 } ) ,
@@ -243,7 +245,6 @@ describe('Execute: Cancellation', () => {
243245 rootValue : {
244246 todo : {
245247 id : '1' ,
246- text : 'Hello, World!' ,
247248 /* c8 ignore next 3 */
248249 author : async ( ) =>
249250 Promise . resolve ( ( ) => expect . fail ( 'Should not be called' ) ) ,
@@ -272,14 +273,153 @@ describe('Execute: Cancellation', () => {
272273 } ) ;
273274 } ) ;
274275
276+ it ( 'should stop the execution when aborted despite a hanging resolver' , async ( ) => {
277+ const abortController = new AbortController ( ) ;
278+ const document = parse ( `
279+ query {
280+ todo {
281+ id
282+ author {
283+ id
284+ }
285+ }
286+ }
287+ ` ) ;
288+
289+ const resultPromise = execute ( {
290+ document,
291+ schema,
292+ abortSignal : abortController . signal ,
293+ rootValue : {
294+ todo : ( ) =>
295+ new Promise ( ( ) => {
296+ /* will never resolve */
297+ } ) ,
298+ } ,
299+ } ) ;
300+
301+ abortController . abort ( ) ;
302+
303+ const result = await resultPromise ;
304+
305+ expect ( result . errors ?. [ 0 ] . originalError ?. name ) . to . equal ( 'AbortError' ) ;
306+
307+ expectJSON ( result ) . toDeepEqual ( {
308+ data : {
309+ todo : null ,
310+ } ,
311+ errors : [
312+ {
313+ message : 'This operation was aborted' ,
314+ path : [ 'todo' ] ,
315+ locations : [ { line : 3 , column : 9 } ] ,
316+ } ,
317+ ] ,
318+ } ) ;
319+ } ) ;
320+
321+ it ( 'should stop the execution when aborted despite a hanging item' , async ( ) => {
322+ const abortController = new AbortController ( ) ;
323+ const document = parse ( `
324+ query {
325+ todo {
326+ id
327+ items
328+ }
329+ }
330+ ` ) ;
331+
332+ const resultPromise = execute ( {
333+ document,
334+ schema,
335+ abortSignal : abortController . signal ,
336+ rootValue : {
337+ todo : ( ) => ( {
338+ id : '1' ,
339+ items : [
340+ new Promise ( ( ) => {
341+ /* will never resolve */
342+ } ) ,
343+ ] ,
344+ } ) ,
345+ } ,
346+ } ) ;
347+
348+ abortController . abort ( ) ;
349+
350+ const result = await resultPromise ;
351+
352+ expect ( result . errors ?. [ 0 ] . originalError ?. name ) . to . equal ( 'AbortError' ) ;
353+
354+ expectJSON ( result ) . toDeepEqual ( {
355+ data : {
356+ todo : {
357+ id : '1' ,
358+ items : [ null ] ,
359+ } ,
360+ } ,
361+ errors : [
362+ {
363+ message : 'This operation was aborted' ,
364+ path : [ 'todo' , 'items' , 0 ] ,
365+ locations : [ { line : 5 , column : 11 } ] ,
366+ } ,
367+ ] ,
368+ } ) ;
369+ } ) ;
370+
371+ it ( 'should stop the execution when aborted with proper null bubbling' , async ( ) => {
372+ const abortController = new AbortController ( ) ;
373+ const document = parse ( `
374+ query {
375+ nonNullableTodo {
376+ id
377+ author {
378+ id
379+ }
380+ }
381+ }
382+ ` ) ;
383+
384+ const resultPromise = execute ( {
385+ document,
386+ schema,
387+ abortSignal : abortController . signal ,
388+ rootValue : {
389+ nonNullableTodo : async ( ) =>
390+ Promise . resolve ( {
391+ id : '1' ,
392+ /* c8 ignore next */
393+ author : ( ) => expect . fail ( 'Should not be called' ) ,
394+ } ) ,
395+ } ,
396+ } ) ;
397+
398+ abortController . abort ( ) ;
399+
400+ const result = await resultPromise ;
401+
402+ expect ( result . errors ?. [ 0 ] . originalError ?. name ) . to . equal ( 'AbortError' ) ;
403+
404+ expectJSON ( result ) . toDeepEqual ( {
405+ data : null ,
406+ errors : [
407+ {
408+ message : 'This operation was aborted' ,
409+ path : [ 'nonNullableTodo' ] ,
410+ locations : [ { line : 3 , column : 9 } ] ,
411+ } ,
412+ ] ,
413+ } ) ;
414+ } ) ;
415+
275416 it ( 'should stop deferred execution when aborted' , async ( ) => {
276417 const abortController = new AbortController ( ) ;
277418 const document = parse ( `
278419 query {
279420 todo {
280421 id
281422 ... on Todo @defer {
282- text
283423 author {
284424 id
285425 }
@@ -295,7 +435,6 @@ describe('Execute: Cancellation', () => {
295435 todo : async ( ) =>
296436 Promise . resolve ( {
297437 id : '1' ,
298- text : 'hello world' ,
299438 /* c8 ignore next */
300439 author : ( ) => expect . fail ( 'Should not be called' ) ,
301440 } ) ,
@@ -341,6 +480,10 @@ describe('Execute: Cancellation', () => {
341480 } ,
342481 } ) ;
343482
483+ await resolveOnNextTick ( ) ;
484+ await resolveOnNextTick ( ) ;
485+ await resolveOnNextTick ( ) ;
486+
344487 abortController . abort ( ) ;
345488
346489 const result = await resultPromise ;
@@ -391,4 +534,39 @@ describe('Execute: Cancellation', () => {
391534 ] ,
392535 } ) ;
393536 } ) ;
537+
538+ it ( 'should stop the execution when aborted during subscription' , async ( ) => {
539+ const abortController = new AbortController ( ) ;
540+ const document = parse ( `
541+ subscription {
542+ foo
543+ }
544+ ` ) ;
545+
546+ const resultPromise = subscribe ( {
547+ document,
548+ schema,
549+ abortSignal : abortController . signal ,
550+ rootValue : {
551+ foo : async ( ) =>
552+ new Promise ( ( ) => {
553+ /* will never resolve */
554+ } ) ,
555+ } ,
556+ } ) ;
557+
558+ abortController . abort ( ) ;
559+
560+ const result = await resultPromise ;
561+
562+ expectJSON ( result ) . toDeepEqual ( {
563+ errors : [
564+ {
565+ message : 'This operation was aborted' ,
566+ path : [ 'foo' ] ,
567+ locations : [ { line : 3 , column : 9 } ] ,
568+ } ,
569+ ] ,
570+ } ) ;
571+ } ) ;
394572} ) ;
0 commit comments