Skip to content

Commit 4f2bba3

Browse files
committed
fix: e2e's amended
1 parent 1f92d1d commit 4f2bba3

7 files changed

Lines changed: 254 additions & 85 deletions

File tree

Lines changed: 162 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,170 @@
1-
const testStreamQuery = /* GraphQL */ `
2-
query StreamQuery($delay: Int) {
3-
streamable(delay: $delay) @stream(initialCount: 2) {
4-
text
5-
}
6-
}
7-
`;
1+
describe('IncrementalDelivery support via fetcher', () => {
2+
describe('When operation contains @stream', () => {
3+
const testStreamQuery = /* GraphQL */ `
4+
query StreamQuery($delay: Int) {
5+
streamable(delay: $delay) @stream(initialCount: 2) {
6+
text
7+
}
8+
}
9+
`;
810

9-
const mockStreamSuccess = {
10-
data: {
11-
streamable: [
12-
{
13-
text: 'Hi',
14-
},
15-
{
16-
text: '你好',
17-
},
18-
{
19-
text: 'Hola',
20-
},
21-
{
22-
text: 'أهلاً',
23-
},
24-
{
25-
text: 'Bonjour',
26-
},
27-
{
28-
text: 'سلام',
29-
},
30-
{
31-
text: '안녕',
32-
},
33-
{
34-
text: 'Ciao',
35-
},
36-
{
37-
text: 'हेलो',
38-
},
39-
{
40-
text: 'Здорово',
11+
const mockStreamSuccess = {
12+
data: {
13+
streamable: [
14+
{
15+
text: 'Hi',
16+
},
17+
{
18+
text: '你好',
19+
},
20+
{
21+
text: 'Hola',
22+
},
23+
{
24+
text: 'أهلاً',
25+
},
26+
{
27+
text: 'Bonjour',
28+
},
29+
{
30+
text: 'سلام',
31+
},
32+
{
33+
text: '안녕',
34+
},
35+
{
36+
text: 'Ciao',
37+
},
38+
{
39+
text: 'हेलो',
40+
},
41+
{
42+
text: 'Здорово',
43+
},
44+
],
4145
},
42-
],
43-
},
44-
hasNext: false,
45-
};
46+
hasNext: false,
47+
};
4648

47-
const testDeferQuery = /* GraphQL */ `
48-
query DeferQuery($delay: Int) {
49-
streamable(delay: $delay) @stream(initialCount: 2) {
50-
text
51-
}
52-
}
53-
`;
49+
it('Expects slower streams to resolve in several increments, and the payloads to patch properly', () => {
50+
const delay = 100;
51+
const timeout = mockStreamSuccess.data.streamable.length * (delay * 1.5);
5452

55-
describe('IncrementalDelivery support via fetcher', () => {
56-
it('Expects slower streams to resolve in several increments, and the payloads to patch properly', () => {
57-
const delay = 100;
58-
const timeout = mockStreamSuccess.data.streamable.length * (delay * 1.5);
53+
cy.visit(`/?query=${testStreamQuery}`);
54+
cy.assertQueryResult(
55+
{ query: testStreamQuery, variables: { delay } },
56+
mockStreamSuccess,
57+
timeout,
58+
);
59+
});
5960

60-
cy.visit(`/?query=${testStreamQuery}`);
61-
cy.assertQueryResult(
62-
{ query: testStreamQuery, variables: { delay } },
63-
mockStreamSuccess,
64-
timeout,
65-
);
61+
it('Expects a quick stream to resolve in a single increment', () => {
62+
cy.visit(`/?query=${testStreamQuery}`);
63+
cy.assertQueryResult(
64+
{ query: testStreamQuery, variables: { delay: 0 } },
65+
mockStreamSuccess,
66+
);
67+
});
6668
});
67-
it('Expects a quick stream to resolve in a single increment', () => {
68-
cy.visit(`/?query=${testStreamQuery}`);
69-
cy.assertQueryResult(
70-
{ query: testStreamQuery, variables: { delay: 0 } },
71-
mockStreamSuccess,
72-
);
69+
70+
describe('When operating with @defer', () => {
71+
it('Excepts to see a slow response but path properly', () => {
72+
const delay = 1000;
73+
const timeout = delay * 1.5;
74+
75+
const testQuery = /* GraphQL */ `
76+
query DeferQuery($delay: Int) {
77+
deferrable {
78+
normalString
79+
... @defer {
80+
deferredString(delay: $delay)
81+
}
82+
}
83+
}
84+
`;
85+
86+
cy.visit(`/?query=${testQuery}`);
87+
cy.assertQueryResult(
88+
{ query: testQuery, variables: { delay } },
89+
{
90+
data: {
91+
deferrable: {
92+
normalString: 'Nice',
93+
deferredString:
94+
'Oops, this took 1 seconds longer than I thought it would!',
95+
},
96+
},
97+
hasNext: false,
98+
},
99+
timeout,
100+
);
101+
});
102+
103+
it('Expects to merge types when members arrive at different times', () => {
104+
/*
105+
This tests that;
106+
1. user ({name}) => { name }
107+
2. user ({age}) => { name, age }
108+
3. user.friends.0 ({name}) => { name, age, friends: [{name}] } <- can sometimes happen before 4, due the the promise race
109+
4. user.friends.0 ({age}) => { name, age, friends: [{name, age}] }
110+
111+
This shows us that we can deep merge defers, deep merge streams, and also deep merge defers inside streams
112+
*/
113+
114+
const delay = 1000;
115+
const timeout = 4 /* friends */ * (delay * 1.5);
116+
117+
const testQuery = /* GraphQL */ `
118+
query DeferQuery($delay: Int) {
119+
person {
120+
name
121+
... @defer {
122+
age(delay: $delay)
123+
}
124+
friends @stream(initialCount: 0) {
125+
... @defer {
126+
name
127+
}
128+
... @defer {
129+
age(delay: $delay)
130+
}
131+
}
132+
}
133+
}
134+
`;
135+
136+
cy.visit(`/?query=${testQuery}`);
137+
cy.assertQueryResult(
138+
{ query: testQuery, variables: { delay } },
139+
{
140+
data: {
141+
person: {
142+
name: 'Mark',
143+
friends: [
144+
{
145+
name: 'James',
146+
age: 1000,
147+
},
148+
{
149+
name: 'Mary',
150+
age: 1000,
151+
},
152+
{
153+
name: 'John',
154+
age: 1000,
155+
},
156+
{
157+
name: 'Patrica',
158+
age: 1000,
159+
},
160+
],
161+
age: 1000,
162+
},
163+
},
164+
hasNext: false,
165+
},
166+
timeout,
167+
);
168+
});
73169
});
74170
});

packages/graphiql/cypress/support/commands.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ declare namespace Cypress {
1717
variablesString?: string;
1818
};
1919
type MockResult =
20-
| {
21-
data: any;
22-
}
20+
| { data: any }
21+
| { data: any; hasNext?: boolean }
2322
| { error: any[] };
2423
interface Chainable<Subject = any> {
2524
/**

packages/graphiql/package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@
4949
"entities": "^2.0.0",
5050
"graphql-language-service": "^3.1.2",
5151
"markdown-it": "^10.0.0",
52-
"@graphiql/toolkit": "^0.1.0",
53-
"deepmerge": "^4.2.2",
54-
"nestie": "^1.0.0"
52+
"@graphiql/toolkit": "^0.1.0"
5553
},
5654
"peerDependencies": {
5755
"graphql": "^14.0.0 || ^15.0.0",

packages/graphiql/src/components/GraphiQL.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,7 @@ import {
5252
introspectionQueryName,
5353
introspectionQuerySansSubscriptions,
5454
} from '../utility/introspectionQueries';
55-
import deepmerge from 'deepmerge';
56-
import { nestie } from 'nestie';
55+
import { dset } from '../utility/dset-merger';
5756

5857
import type {
5958
Fetcher,
@@ -1114,11 +1113,8 @@ export class GraphiQL extends React.Component<GraphiQLProps, GraphiQLState> {
11141113
`Expected part to contain a data property, but got ${part}`,
11151114
);
11161115
}
1117-
const partData = nestie<any, any>({
1118-
[part.path.join('.')]: part.data,
1119-
});
11201116

1121-
fullResponse.data = deepmerge(fullResponse.data, partData);
1117+
dset(payload.data, path, data);
11221118
} else if (data) {
11231119
// If there is no path, we don't know what to do with the payload,
11241120
// so we just set it.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// @ts-nocheck
2+
3+
/*
4+
!!IMPORTANT
5+
This is a _fork_ of https://github.com/lukeed/dset/ that adds support in for deep-merging collisions. Once `dset/merge` becomes a thing we can remove this.
6+
7+
@maraisr will be monitoring this.
8+
*/
9+
10+
export function dset<T extends object, V>(
11+
obj: T,
12+
keys: string | ArrayLike<string | number>,
13+
value: V,
14+
): void;
15+
export function dset(obj, keys, val) {
16+
keys.split && (keys = keys.split('.'));
17+
let i = 0;
18+
const l = keys.length;
19+
let t = obj;
20+
let x;
21+
let k;
22+
for (; i < l; ) {
23+
k = keys[i++];
24+
if (k === '__proto__' || k === 'constructor' || k === 'prototype') {
25+
break;
26+
}
27+
t = t[k] =
28+
i === l
29+
? merge(t[k], val) // Note; this here was the change.
30+
: typeof (x = t[k]) === typeof keys
31+
? x
32+
: keys[i] * 0 !== 0 || Boolean(~String(keys[i]).indexOf('.'))
33+
? {}
34+
: [];
35+
}
36+
}
37+
38+
function merge(a, b) {
39+
if (typeof a === 'object' && typeof b === 'object') {
40+
if (Array.isArray(a) && Array.isArray(b)) {
41+
for (let i = 0; i < b.length; i++) {
42+
a[i] = merge(a[i], b[i]);
43+
}
44+
} else {
45+
for (const k in b) {
46+
a[k] = merge(a[k], b[k]);
47+
}
48+
}
49+
return a;
50+
}
51+
return b;
52+
}

packages/graphiql/test/schema.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,37 @@ const DeferrableObject = new GraphQLObjectType({
165165
},
166166
});
167167

168+
const Person = new GraphQLObjectType({
169+
name: 'Person',
170+
fields: () => ({
171+
name: {
172+
type: GraphQLString,
173+
resolve: obj => obj.name,
174+
},
175+
age: {
176+
args: {
177+
delay: delayArgument(600),
178+
},
179+
type: GraphQLInt,
180+
resolve: async function lazilyReturnValue(_value, args) {
181+
const seconds = args.delay / 1000;
182+
await sleep(args.delay);
183+
return Math.ceil(args.delay);
184+
},
185+
},
186+
friends: {
187+
type: new GraphQLList(Person),
188+
async *resolve(_value, args) {
189+
const names = ['James', 'Mary', 'John', 'Patrica']; // Top 4 names https://www.ssa.gov/oact/babynames/decades/century.html
190+
for (const name of names) {
191+
await sleep(100);
192+
yield { name };
193+
}
194+
},
195+
},
196+
}),
197+
});
198+
168199
const sleep = async timeout => new Promise(res => setTimeout(res, timeout));
169200

170201
const TestType = new GraphQLObjectType({
@@ -206,6 +237,10 @@ const TestType = new GraphQLObjectType({
206237
}
207238
},
208239
},
240+
person: {
241+
type: Person,
242+
resolve: () => ({ name: 'Mark' }),
243+
},
209244
longDescriptionType: {
210245
type: TestType,
211246
description:

0 commit comments

Comments
 (0)