diff --git a/src/execution/incremental/__tests__/defer-test.ts b/src/execution/incremental/__tests__/defer-test.ts index c4c325b95a..f311b1c707 100644 --- a/src/execution/incremental/__tests__/defer-test.ts +++ b/src/execution/incremental/__tests__/defer-test.ts @@ -260,6 +260,76 @@ describe('Execute: defer directive', () => { }, ]); }); + it('Returns label from defer directive', async () => { + const document = parse(` + query HeroNameQuery { + hero { + id + ...NameFragment @defer(label: "defer-label") + } + } + fragment NameFragment on Hero { + name + } + `); + const result = await complete(document); + + expectJSON(result).toDeepEqual([ + { + data: { + hero: { + id: '1', + }, + }, + pending: [{ id: '0', path: ['hero'], label: 'defer-label' }], + hasNext: true, + }, + { + incremental: [ + { + data: { + name: 'Luke', + }, + id: '0', + }, + ], + completed: [{ id: '0' }], + hasNext: false, + }, + ]); + }); + it('Treats null defer label the same as no label', async () => { + const document = parse(` + query HeroNameQuery { + hero { + id + ...NameFragment @defer(label: null) + } + } + fragment NameFragment on Hero { + name + } + `); + const result = await complete(document); + + expectJSON(result).toDeepEqual([ + { + data: { hero: { id: '1' } }, + pending: [{ id: '0', path: ['hero'] }], + hasNext: true, + }, + { + incremental: [ + { + data: { name: 'Luke' }, + id: '0', + }, + ], + completed: [{ id: '0' }], + hasNext: false, + }, + ]); + }); it('Can disable defer using if argument', async () => { const document = parse(` query HeroNameQuery { diff --git a/src/execution/incremental/__tests__/stream-test.ts b/src/execution/incremental/__tests__/stream-test.ts index 9f92f92e60..6ccdc21228 100644 --- a/src/execution/incremental/__tests__/stream-test.ts +++ b/src/execution/incremental/__tests__/stream-test.ts @@ -326,6 +326,33 @@ describe('Execute: stream directive', () => { }, ]); }); + it('Treats null stream label the same as no label', async () => { + const document = parse( + '{ scalarList @stream(initialCount: 1, label: null) }', + ); + const result = await complete(document, { + scalarList: () => ['apple', 'banana', 'coconut'], + }); + expectJSON(result).toDeepEqual([ + { + data: { + scalarList: ['apple'], + }, + pending: [{ id: '0', path: ['scalarList'] }], + hasNext: true, + }, + { + incremental: [ + { + items: ['banana', 'coconut'], + id: '0', + }, + ], + completed: [{ id: '0' }], + hasNext: false, + }, + ]); + }); it('Can disable @stream using if argument', async () => { const document = parse( '{ scalarList @stream(initialCount: 0, if: false) }', diff --git a/src/execution/legacyIncremental/__tests__/legacy-defer-test.ts b/src/execution/legacyIncremental/__tests__/legacy-defer-test.ts index 7215674b57..1b4ba1dc37 100644 --- a/src/execution/legacyIncremental/__tests__/legacy-defer-test.ts +++ b/src/execution/legacyIncremental/__tests__/legacy-defer-test.ts @@ -259,6 +259,73 @@ describe('Execute: defer directive (legacy)', () => { }, ]); }); + it('Returns label from defer directive', async () => { + const document = parse(` + query HeroNameQuery { + hero { + id + ...NameFragment @defer(label: "defer-label") + } + } + fragment NameFragment on Hero { + name + } + `); + const result = await complete(document); + + expectJSON(result).toDeepEqual([ + { + data: { + hero: { + id: '1', + }, + }, + hasNext: true, + }, + { + incremental: [ + { + data: { + name: 'Luke', + }, + path: ['hero'], + label: 'defer-label', + }, + ], + hasNext: false, + }, + ]); + }); + it('Treats null defer label the same as no label', async () => { + const document = parse(` + query HeroNameQuery { + hero { + id + ...NameFragment @defer(label: null) + } + } + fragment NameFragment on Hero { + name + } + `); + const result = await complete(document); + + expectJSON(result).toDeepEqual([ + { + data: { hero: { id: '1' } }, + hasNext: true, + }, + { + incremental: [ + { + data: { name: 'Luke' }, + path: ['hero'], + }, + ], + hasNext: false, + }, + ]); + }); it('Can disable defer using if argument', async () => { const document = parse(` query HeroNameQuery { diff --git a/src/execution/legacyIncremental/__tests__/legacy-stream-test.ts b/src/execution/legacyIncremental/__tests__/legacy-stream-test.ts index ceaa05d587..1cb198fe44 100644 --- a/src/execution/legacyIncremental/__tests__/legacy-stream-test.ts +++ b/src/execution/legacyIncremental/__tests__/legacy-stream-test.ts @@ -284,6 +284,31 @@ describe('Execute: stream directive (legacy)', () => { }, ]); }); + it('Treats null stream label the same as no label', async () => { + const document = parse( + '{ scalarList @stream(initialCount: 1, label: null) }', + ); + const result = await complete(document, { + scalarList: () => ['apple', 'banana', 'coconut'], + }); + expectJSON(result).toDeepEqual([ + { + data: { + scalarList: ['apple'], + }, + hasNext: true, + }, + { + incremental: [ + { + items: ['banana', 'coconut'], + path: ['scalarList', 1], + }, + ], + hasNext: false, + }, + ]); + }); it('Can disable @stream using if argument', async () => { const document = parse( '{ scalarList @stream(initialCount: 0, if: false) }', diff --git a/src/validation/__tests__/DeferStreamDirectiveLabelRule-test.ts b/src/validation/__tests__/DeferStreamDirectiveLabelRule-test.ts index 4126c6e578..6103e40c8a 100644 --- a/src/validation/__tests__/DeferStreamDirectiveLabelRule-test.ts +++ b/src/validation/__tests__/DeferStreamDirectiveLabelRule-test.ts @@ -47,6 +47,19 @@ describe('Validate: Defer/Stream directive labels', () => { `); }); + it('Defer fragment with null label', () => { + expectValid(` + { + dog { + ...dogFragmentA @defer(label: null) + } + } + fragment dogFragmentA on Dog { + name + } + `); + }); + it('Defer fragment with variable label', () => { expectErrors(` query($label: String) { @@ -125,6 +138,15 @@ describe('Validate: Defer/Stream directive labels', () => { } `); }); + it('Stream with null label', () => { + expectValid(` + { + pets @stream(label: null) { + name + } + } + `); + }); it('Stream with variable label', () => { expectErrors(` query ($label: String!) { diff --git a/src/validation/rules/DeferStreamDirectiveLabelRule.ts b/src/validation/rules/DeferStreamDirectiveLabelRule.ts index 2b6d35a816..02cb399a74 100644 --- a/src/validation/rules/DeferStreamDirectiveLabelRule.ts +++ b/src/validation/rules/DeferStreamDirectiveLabelRule.ts @@ -30,7 +30,7 @@ export function DeferStreamDirectiveLabelRule( (arg) => arg.name.value === 'label', ); const labelValue = labelArgument?.value; - if (!labelValue) { + if (!labelValue || labelValue.kind === Kind.NULL) { return; } if (labelValue.kind !== Kind.STRING) {