@hyper-fetch/graphql 7.5.2 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/{.eslintrc.json → .eslintrc copy.json } +1 -1
  2. package/.eslintrc.cjs +12 -0
  3. package/__tests__/constants/mutations.constants.ts +15 -0
  4. package/__tests__/constants/queries.constants.ts +24 -0
  5. package/__tests__/e2e/e2e.graphql-server.ts +135 -0
  6. package/__tests__/e2e/e2e.graphql.spec.ts +486 -0
  7. package/__tests__/features/adapter/adapter.browser.spec.ts +259 -0
  8. package/__tests__/features/adapter/adapter.server.spec.ts +234 -0
  9. package/__tests__/features/adapter/adapter.utils.spec.ts +86 -0
  10. package/__tests__/request-module.d.ts +12 -0
  11. package/__tests__/shared/callbacks.shared.ts +11 -0
  12. package/__tests__/shared/index.ts +2 -0
  13. package/__tests__/shared/progress.shared.ts +40 -0
  14. package/__tests__/tsconfig.json +23 -0
  15. package/__tests__/vitest.setup.ts +10 -0
  16. package/dist/adapter/adapter.constants.d.ts +4 -0
  17. package/dist/adapter/adapter.constants.d.ts.map +1 -0
  18. package/dist/adapter/adapter.d.ts +3 -0
  19. package/dist/adapter/adapter.d.ts.map +1 -0
  20. package/dist/adapter/adapter.types.d.ts +24 -0
  21. package/dist/adapter/adapter.types.d.ts.map +1 -0
  22. package/dist/adapter/adapter.utils.d.ts +9 -0
  23. package/dist/adapter/adapter.utils.d.ts.map +1 -0
  24. package/dist/adapter/http-adapter.fetch.d.ts +3 -0
  25. package/dist/adapter/http-adapter.fetch.d.ts.map +1 -0
  26. package/dist/adapter/index.d.ts +6 -0
  27. package/dist/adapter/index.d.ts.map +1 -0
  28. package/dist/index.d.ts +2 -34
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.mjs +183 -0
  31. package/dist/index.mjs.map +1 -0
  32. package/package.json +10 -25
  33. package/project.json +4 -0
  34. package/src/adapter/adapter.constants.ts +1 -1
  35. package/src/adapter/adapter.ts +3 -8
  36. package/src/adapter/adapter.types.ts +7 -3
  37. package/src/adapter/adapter.utils.ts +8 -3
  38. package/src/adapter/http-adapter.fetch.ts +186 -0
  39. package/src/adapter/index.ts +1 -0
  40. package/tsconfig.base.json +2 -6
  41. package/tsconfig.json +2 -6
  42. package/vite.config.ts +45 -0
  43. package/dist/browser/index.js +0 -191
  44. package/dist/browser/index.js.map +0 -7
  45. package/dist/index.js +0 -330
  46. package/dist/index.js.map +0 -7
  47. package/src/adapter/http-adapter.browser.ts +0 -148
  48. package/src/adapter/http-adapter.server.ts +0 -148
@@ -0,0 +1,259 @@
1
+ import { Client, getErrorMessage } from "@hyper-fetch/core";
2
+ import { createGraphqlMockingServer } from "@hyper-fetch/testing";
3
+
4
+ import { GraphqlAdapter } from "adapter";
5
+ import type { GetUserQueryResponse } from "../../constants/queries.constants";
6
+ import { getUserQuery, getUserQueryString } from "../../constants/queries.constants";
7
+ import type { LoginMutationVariables } from "../../constants/mutations.constants";
8
+ import { loginMutation } from "../../constants/mutations.constants";
9
+
10
+ const { startServer, stopServer, resetMocks, mockRequest } = createGraphqlMockingServer();
11
+
12
+ describe("Graphql Adapter [ Browser ]", () => {
13
+ let client = new Client({ url: "https://shared-base-url/graphql" }).setAdapter(GraphqlAdapter());
14
+ let request = client.createRequest<{ response: GetUserQueryResponse }>()({ endpoint: getUserQuery });
15
+ let mutation = client.createRequest<{ response: GetUserQueryResponse; payload: LoginMutationVariables }>()({
16
+ endpoint: loginMutation,
17
+ });
18
+
19
+ beforeAll(() => {
20
+ startServer();
21
+ });
22
+
23
+ beforeEach(() => {
24
+ client = new Client({ url: "https://shared-base-url/graphql" }).setAdapter(GraphqlAdapter());
25
+ request = client.createRequest<{ response: GetUserQueryResponse }>()({ endpoint: getUserQuery });
26
+ mutation = client.createRequest<{ response: GetUserQueryResponse; payload: LoginMutationVariables }>()({
27
+ endpoint: loginMutation,
28
+ });
29
+
30
+ resetMocks();
31
+ vi.resetAllMocks();
32
+ });
33
+
34
+ afterAll(() => {
35
+ stopServer();
36
+ });
37
+
38
+ it("should make a request and return success data with status", async () => {
39
+ const expected = mockRequest(request, { data: { username: "prc", firstName: "Maciej" } });
40
+
41
+ const { data, error, status, extra } = await request.send();
42
+
43
+ expect(expected.data).toStrictEqual(data);
44
+ expect(status).toBe(200);
45
+ expect(error).toBe(null);
46
+ expect(extra).toStrictEqual({
47
+ headers: { "content-type": "application/json", "content-length": "48" },
48
+ extensions: {},
49
+ });
50
+ });
51
+
52
+ it("should make a request with string endpoint", async () => {
53
+ const expected = mockRequest(request, { data: { username: "prc", firstName: "Maciej" } });
54
+
55
+ const { data, error, status, extra } = await client.createRequest()({ endpoint: getUserQueryString }).send({});
56
+
57
+ expect(expected.data).toStrictEqual(data);
58
+ expect(status).toBe(200);
59
+ expect(error).toBe(null);
60
+ expect(extra).toStrictEqual({
61
+ headers: { "content-type": "application/json", "content-length": "48" },
62
+ extensions: {},
63
+ });
64
+ });
65
+
66
+ it("should make a request and return error data with status", async () => {
67
+ const expected = mockRequest(request, { status: 400 });
68
+
69
+ const { data, error, status, extra } = await request.send();
70
+
71
+ expect(data).toBe(null);
72
+ expect(status).toBe(400);
73
+ expect(error).toStrictEqual(expected.errors);
74
+ expect(extra).toStrictEqual({
75
+ headers: { "content-type": "application/json", "content-length": "42" },
76
+ extensions: {},
77
+ });
78
+ });
79
+
80
+ it("should allow to make mutation request", async () => {
81
+ const expected = mockRequest(
82
+ mutation.setPayload({
83
+ username: "Kacper",
84
+ password: "Kacper1234",
85
+ }),
86
+ { data: { username: "prc", firstName: "Maciej" } },
87
+ );
88
+
89
+ const { data, error, status, extra } = await mutation
90
+ .setPayload({
91
+ username: "Kacper",
92
+ password: "Kacper1234",
93
+ })
94
+ .send();
95
+
96
+ expect(expected.data).toStrictEqual(data);
97
+ expect(status).toBe(200);
98
+ expect(error).toBe(null);
99
+ expect(extra).toStrictEqual({
100
+ headers: { "content-type": "application/json", "content-length": "48" },
101
+ extensions: {},
102
+ });
103
+ });
104
+
105
+ it("should allow to cancel request and return error", async () => {
106
+ mockRequest(request, { delay: 5 });
107
+
108
+ setTimeout(() => {
109
+ request.abort();
110
+ }, 2);
111
+
112
+ const { data, error } = await request.send();
113
+
114
+ expect(data).toBe(null);
115
+ expect(error).toStrictEqual([getErrorMessage("abort")]);
116
+ });
117
+
118
+ // it("should not throw when XMLHttpRequest is not available on window", async () => {
119
+ // const expected = mockRequest(request, { delay: 20 });
120
+ // const xml = window.XMLHttpRequest;
121
+ // window.XMLHttpRequest = undefined as any;
122
+
123
+ // const { data, error, status, extra } = await request.send();
124
+
125
+ // expect(expected.data).toStrictEqual(data);
126
+ // expect(status).toBe(200);
127
+ // expect(error).toBe(null);
128
+ // expect(extra).toMatchObject({
129
+ // headers: { "content-type": "application/json", "content-length": "11" },
130
+ // extensions: {},
131
+ // });
132
+ // window.XMLHttpRequest = xml;
133
+ // });
134
+
135
+ it("should allow to set options and timeout the request", async () => {
136
+ const timeoutRequest = request.setOptions({ timeout: 5 });
137
+ mockRequest(timeoutRequest, { delay: 500 });
138
+
139
+ const { data, error, status } = await timeoutRequest.send();
140
+
141
+ expect(data).toBeNull();
142
+ expect(status).toBe(0);
143
+ expect(error).toStrictEqual(getErrorMessage("timeout"));
144
+ });
145
+
146
+ it("should handle undefined response data as null", async () => {
147
+ mockRequest(request, { data: null });
148
+ const { data } = await request.send();
149
+ expect(data).toBe(null);
150
+ });
151
+
152
+ it("should use default error message when errors are null", async () => {
153
+ mockRequest(request, {
154
+ status: 400,
155
+ error: null,
156
+ });
157
+
158
+ const { error } = await request.send();
159
+ expect(error).toStrictEqual([getErrorMessage()]);
160
+ });
161
+
162
+ it("should fallback to response.text() when body.getReader is not available", async () => {
163
+ const responseData = JSON.stringify({ data: { username: "prc", firstName: "Maciej" } });
164
+ const originalFetch = global.fetch;
165
+ global.fetch = vi.fn().mockResolvedValue({
166
+ ok: true,
167
+ status: 200,
168
+ headers: new Headers({ "content-type": "application/json" }),
169
+ body: null,
170
+ text: () => Promise.resolve(responseData),
171
+ });
172
+
173
+ const { data, status, error } = await request.send();
174
+
175
+ expect(status).toBe(200);
176
+ expect(data).toStrictEqual({ username: "prc", firstName: "Maciej" });
177
+ expect(error).toBe(null);
178
+
179
+ global.fetch = originalFetch;
180
+ });
181
+
182
+ it("should handle network error in catch block when not aborted", async () => {
183
+ const originalFetch = global.fetch;
184
+ const networkError = new Error("Network failure");
185
+ global.fetch = vi.fn().mockRejectedValue(networkError);
186
+
187
+ const { data, error, status } = await request.send();
188
+
189
+ expect(data).toBeNull();
190
+ expect(status).toBe(0);
191
+ expect(error).toBe(networkError);
192
+
193
+ global.fetch = originalFetch;
194
+ });
195
+
196
+ it("should handle request with abort controller present", async () => {
197
+ mockRequest(request, { data: { username: "prc", firstName: "Maciej" } });
198
+
199
+ const { data, status, error } = await request.send();
200
+
201
+ expect(status).toBe(200);
202
+ expect(data).toStrictEqual({ username: "prc", firstName: "Maciej" });
203
+ expect(error).toBe(null);
204
+ });
205
+
206
+ it("should handle response with content-length header for progress tracking", async () => {
207
+ const responseData = JSON.stringify({ data: { username: "prc", firstName: "Maciej" } });
208
+ const encoder = new TextEncoder();
209
+ const encoded = encoder.encode(responseData);
210
+ const originalFetch = global.fetch;
211
+
212
+ let readerDone = false;
213
+ global.fetch = vi.fn().mockResolvedValue({
214
+ ok: true,
215
+ status: 200,
216
+ headers: new Headers({
217
+ "content-type": "application/json",
218
+ "content-length": String(encoded.length),
219
+ }),
220
+ body: {
221
+ getReader: () => ({
222
+ read: () => {
223
+ if (!readerDone) {
224
+ readerDone = true;
225
+ return Promise.resolve({ done: false, value: encoded });
226
+ }
227
+ return Promise.resolve({ done: true, value: undefined });
228
+ },
229
+ }),
230
+ },
231
+ });
232
+
233
+ const { data, status } = await request.send();
234
+
235
+ expect(status).toBe(200);
236
+ expect(data).toStrictEqual({ username: "prc", firstName: "Maciej" });
237
+
238
+ global.fetch = originalFetch;
239
+ });
240
+
241
+ it("should handle failure response when body has no getReader", async () => {
242
+ const originalFetch = global.fetch;
243
+ global.fetch = vi.fn().mockResolvedValue({
244
+ ok: false,
245
+ status: 500,
246
+ headers: new Headers({ "content-type": "application/json" }),
247
+ body: null,
248
+ text: () => Promise.resolve(JSON.stringify({ errors: ["Internal Server Error"] })),
249
+ });
250
+
251
+ const { data, error, status } = await request.send();
252
+
253
+ expect(data).toBeNull();
254
+ expect(status).toBe(500);
255
+ expect(error).toStrictEqual(["Internal Server Error"]);
256
+
257
+ global.fetch = originalFetch;
258
+ });
259
+ });
@@ -0,0 +1,234 @@
1
+ /**
2
+ * @vitest-environment node
3
+ */
4
+ import { Client, getErrorMessage } from "@hyper-fetch/core";
5
+ import { createGraphqlMockingServer } from "@hyper-fetch/testing";
6
+
7
+ import { GraphqlAdapter, GraphqlMethod } from "../../../src/adapter";
8
+ import type { GetUserQueryResponse } from "../../constants/queries.constants";
9
+ import { getUserQuery, getUserQueryString } from "../../constants/queries.constants";
10
+ import type { LoginMutationVariables } from "../../constants/mutations.constants";
11
+ import { loginMutation } from "../../constants/mutations.constants";
12
+
13
+ const { startServer, stopServer, resetMocks, mockRequest } = createGraphqlMockingServer();
14
+
15
+ describe("Graphql Adapter [ Server ]", () => {
16
+ const requestId = "test";
17
+ let clientHttp = new Client({ url: "http://shared-base-url/graphql" }).setAdapter(GraphqlAdapter()).setDebug(true);
18
+ let client = new Client({ url: "https://shared-base-url/graphql" }).setAdapter(GraphqlAdapter()).setDebug(true);
19
+ let requestHttp = clientHttp.createRequest<{ response: GetUserQueryResponse }>()({ endpoint: getUserQuery });
20
+ let request = client.createRequest<{ response: GetUserQueryResponse }>()({ endpoint: getUserQuery });
21
+ let mutation = client.createRequest<{ response: GetUserQueryResponse; payload: LoginMutationVariables }>()({
22
+ endpoint: loginMutation,
23
+ });
24
+
25
+ beforeAll(() => {
26
+ startServer();
27
+ });
28
+
29
+ beforeEach(() => {
30
+ clientHttp = new Client({ url: "http://shared-base-url/graphql" }).setAdapter(GraphqlAdapter());
31
+ client = new Client({ url: "https://shared-base-url/graphql" }).setAdapter(GraphqlAdapter());
32
+ requestHttp = clientHttp.createRequest<{ response: GetUserQueryResponse }>()({ endpoint: getUserQuery });
33
+ request = client.createRequest<{ response: GetUserQueryResponse }>()({ endpoint: getUserQuery });
34
+ mutation = client.createRequest<{ response: GetUserQueryResponse; payload: LoginMutationVariables }>()({
35
+ endpoint: loginMutation,
36
+ });
37
+ client.appManager.isBrowser = false;
38
+ });
39
+
40
+ afterEach(() => {
41
+ client.clear();
42
+ vi.resetAllMocks();
43
+ vi.clearAllMocks();
44
+ vi.restoreAllMocks();
45
+ resetMocks();
46
+ });
47
+
48
+ afterAll(() => {
49
+ stopServer();
50
+ });
51
+
52
+ it("should pick correct adapter and not throw", async () => {
53
+ mockRequest(request);
54
+ client.appManager.isBrowser = true;
55
+
56
+ const adapter = GraphqlAdapter();
57
+
58
+ expect(adapter).toBeDefined();
59
+ });
60
+
61
+ it("should pick http module", async () => {
62
+ mockRequest(requestHttp);
63
+ clientHttp.requestManager.addAbortController(requestHttp.abortKey, requestId);
64
+
65
+ const res = await clientHttp.adapter.fetch(
66
+ clientHttp.createRequest<{ response: GetUserQueryResponse }>()({ endpoint: getUserQuery }),
67
+ requestId,
68
+ );
69
+
70
+ expect(res.data).toBeDefined();
71
+ });
72
+
73
+ it("should handle http and https urls", async () => {
74
+ mockRequest(requestHttp);
75
+
76
+ const res = await requestHttp.send();
77
+
78
+ expect(res.data).toBeDefined();
79
+ });
80
+
81
+ it("should make a request and return success data with status", async () => {
82
+ const expected = mockRequest(request, { delay: 10, data: { username: "prc", firstName: "Maciej" } });
83
+
84
+ const { data, error, status, extra } = await request.send();
85
+
86
+ expect(error).toBe(null);
87
+ expect(expected.data).toStrictEqual(data);
88
+ expect(status).toBe(200);
89
+ expect(extra).toMatchObject({
90
+ headers: { "content-type": "application/json", "content-length": "48" },
91
+ extensions: {},
92
+ });
93
+ });
94
+
95
+ it("should make a request with string endpoint", async () => {
96
+ const expected = mockRequest(request, { data: { username: "prc", firstName: "Maciej" } });
97
+
98
+ const { data, error, status, extra } = await client.createRequest()({ endpoint: getUserQueryString }).send({});
99
+
100
+ expect(expected.data).toStrictEqual(data);
101
+ expect(status).toBe(200);
102
+ expect(error).toBe(null);
103
+ expect(extra).toMatchObject({
104
+ headers: { "content-type": "application/json", "content-length": "48" },
105
+ extensions: {},
106
+ });
107
+ });
108
+ it("should make a get request with string endpoint", async () => {
109
+ const req = client.createRequest<{ response: GetUserQueryResponse }>()({
110
+ endpoint: getUserQueryString,
111
+ method: GraphqlMethod.GET,
112
+ });
113
+ const expected = mockRequest(req, { data: { username: "prc", firstName: "Maciej" } });
114
+
115
+ const { data, error, status, extra } = await req.send({});
116
+
117
+ expect(expected.data).toStrictEqual(data);
118
+ expect(status).toBe(200);
119
+ expect(error).toBe(null);
120
+ expect(extra).toMatchObject({
121
+ headers: { "content-type": "application/json", "content-length": "48" },
122
+ extensions: {},
123
+ });
124
+ });
125
+
126
+ it("should make a request and return error data with status", async () => {
127
+ const expected = mockRequest(request, { status: 400 });
128
+
129
+ const { data, error, status, extra } = await request.send();
130
+
131
+ expect(data).toBe(null);
132
+ expect(status).toBe(400);
133
+ expect(error).toStrictEqual(expected.errors);
134
+ expect(extra).toMatchObject({
135
+ headers: { "content-type": "application/json", "content-length": "42" },
136
+ extensions: {},
137
+ });
138
+ });
139
+
140
+ it("should allow to make mutation request", async () => {
141
+ const req = mutation.setPayload({
142
+ username: "Kacper",
143
+ password: "Kacper1234",
144
+ });
145
+ const expected = mockRequest(req, { data: { username: "prc", firstName: "Maciej" } });
146
+
147
+ const { data, error, status, extra } = await req.send();
148
+
149
+ expect(data).toStrictEqual(expected.data);
150
+ expect(status).toBe(200);
151
+ expect(error).toBe(null);
152
+ expect(extra).toMatchObject({
153
+ headers: { "content-type": "application/json", "content-length": "48" },
154
+ extensions: {},
155
+ });
156
+ });
157
+
158
+ it("should handle undefined response data as null", async () => {
159
+ mockRequest(request, { data: null });
160
+ const { data } = await request.send();
161
+ expect(data).toBe(null);
162
+ });
163
+
164
+ it("should use default error message when errors are null", async () => {
165
+ mockRequest(request, {
166
+ status: 400,
167
+ error: null,
168
+ });
169
+
170
+ const { error } = await request.send();
171
+ expect(error).toStrictEqual([getErrorMessage()]);
172
+ });
173
+
174
+ it("should allow to set options and timeout the request", async () => {
175
+ const timeoutRequest = request.setOptions({ timeout: 5 });
176
+ mockRequest(timeoutRequest, { delay: 500 });
177
+
178
+ const { data, error, status } = await timeoutRequest.send();
179
+
180
+ expect(data).toBeNull();
181
+ expect(status).toBe(0);
182
+ expect(error).toStrictEqual(getErrorMessage("timeout"));
183
+ });
184
+
185
+ // it("should allow to calculate payload size", async () => {
186
+ // let receivedOptions: any;
187
+
188
+ // vi.spyOn(https, "request").mockImplementation((url, options, callback) => {
189
+ // receivedOptions = options;
190
+ // return requestCopy(options, callback);
191
+ // });
192
+ // mockRequest(mutation);
193
+
194
+ // await mutation.send({
195
+ // payload: {
196
+ // username: "Kacper",
197
+ // password: "Kacper1234",
198
+ // },
199
+ // });
200
+
201
+ // expect(receivedOptions.headers["Content-Length"]).toBeGreaterThan(0);
202
+ // });
203
+
204
+ // it("should allow to calculate Buffer size", async () => {
205
+ // let receivedOptions: any;
206
+
207
+ // vi.spyOn(https, "request").mockImplementation((url, options, callback) => {
208
+ // receivedOptions = options;
209
+ // return requestCopy(options, callback);
210
+ // });
211
+ // mockRequest(mutation);
212
+
213
+ // const buffer = Buffer.from("test");
214
+
215
+ // await mutation.send({
216
+ // payload: buffer as any,
217
+ // });
218
+
219
+ // expect(receivedOptions.headers["Content-Length"]).toBeGreaterThan(0);
220
+ // });
221
+
222
+ it("should allow to cancel request and return error", async () => {
223
+ mockRequest(request, { delay: 5 });
224
+
225
+ setTimeout(() => {
226
+ request.abort();
227
+ }, 2);
228
+
229
+ const { data, error } = await request.send();
230
+
231
+ expect(data).toBe(null);
232
+ expect(error).toStrictEqual([getErrorMessage("abort")]);
233
+ });
234
+ });
@@ -0,0 +1,86 @@
1
+ import { Client } from "@hyper-fetch/core";
2
+
3
+ import { GraphqlMethod, getRequestValues, GraphqlAdapter } from "adapter";
4
+ import { gqlEndpointNameMapper } from "../../../src/adapter/adapter.utils";
5
+ import { getUserQuery, getUserQueryString } from "../../constants/queries.constants";
6
+
7
+ describe("Graphql Adapter [ Utils ]", () => {
8
+ let client = new Client({ url: "https://shared-base-url/graphql" }).setAdapter(GraphqlAdapter());
9
+ let request = client.createRequest<{ response: any; payload: any }>()({
10
+ endpoint: getUserQuery,
11
+ method: GraphqlMethod.POST,
12
+ });
13
+ let requestGet = client.createRequest<{ response: any; payload: any }>()({
14
+ endpoint: getUserQueryString,
15
+ method: GraphqlMethod.GET,
16
+ });
17
+
18
+ beforeEach(() => {
19
+ client = new Client({ url: "https://shared-base-url/graphql" }).setAdapter(GraphqlAdapter());
20
+ request = client.createRequest<{ response: any; payload: any }>()({
21
+ endpoint: getUserQuery,
22
+ method: GraphqlMethod.POST,
23
+ });
24
+ requestGet = client.createRequest<{ response: any; payload: any }>()({
25
+ endpoint: getUserQueryString,
26
+ method: GraphqlMethod.GET,
27
+ });
28
+
29
+ vi.resetAllMocks();
30
+ vi.clearAllMocks();
31
+ vi.restoreAllMocks();
32
+ });
33
+
34
+ describe("When using getRequestValues", () => {
35
+ it("should generate values", async () => {
36
+ const { fullUrl, payload } = getRequestValues(request);
37
+
38
+ expect(fullUrl).toBe("https://shared-base-url/graphql");
39
+ expect(payload).toBe('{"query":"query GetUser {\\n username {\\n username\\n firstName\\n }\\n}"}');
40
+ expect(request.method).toBe("POST");
41
+ expect(request.endpoint).toBeString();
42
+ });
43
+ it("should generate values for GET", async () => {
44
+ const { fullUrl, payload } = getRequestValues(requestGet);
45
+
46
+ expect(fullUrl).toBe(
47
+ "https://shared-base-url/graphql?query=%0Aquery%20GetUser%20%7B%0A%20%20username%20%7B%0A%20%20%20%20username%0A%20%20%20%20firstName%0A%20%20%7D%0A%7D%0A",
48
+ );
49
+ expect(payload).toBeNull();
50
+ expect(requestGet.method).toBe("GET");
51
+ expect(requestGet.endpoint).toBeString();
52
+ });
53
+ });
54
+
55
+ describe("When using gqlEndpointNameMapper", () => {
56
+ it("should return operation name for a named query", () => {
57
+ const result = gqlEndpointNameMapper("query GetUser { username { username firstName } }");
58
+ expect(result).toBe("GetUser");
59
+ });
60
+
61
+ it("should return field name when operation has no name", () => {
62
+ const result = gqlEndpointNameMapper("query { username { username firstName } }");
63
+ expect(result).toBe("username");
64
+ });
65
+
66
+ it("should fallback when operation has no name and selection is not a Field", () => {
67
+ const result = gqlEndpointNameMapper("query { ... on User { username } }");
68
+ expect(result).toBe("User");
69
+ });
70
+
71
+ it("should return 'graphql-request' when endpoint is empty after cleanup", () => {
72
+ const result = gqlEndpointNameMapper("{}");
73
+ expect(result).toBe("graphql-request");
74
+ });
75
+
76
+ it("should use fallback when parse throws (invalid graphql)", () => {
77
+ const result = gqlEndpointNameMapper("not a valid graphql string at all");
78
+ expect(result).toBe("not");
79
+ });
80
+
81
+ it("should return 'graphql-request' when entire fallback string is empty", () => {
82
+ const result = gqlEndpointNameMapper("query");
83
+ expect(result).toBe("graphql-request");
84
+ });
85
+ });
86
+ });
@@ -0,0 +1,12 @@
1
+ /** Ambient shim: core uses `baseUrl` `request` → `src/request`; gql test tsconfig resolves bare `request` to npm. */
2
+ declare module "request" {
3
+ export type RequestInstance = object;
4
+
5
+ export function getProgressValue(event: { loaded?: number; total?: number }): number;
6
+
7
+ export function getRequestEta(
8
+ startDate: Date,
9
+ progressDate: Date,
10
+ event: { total: number; loaded: number },
11
+ ): { sizeLeft: number; timeLeft: number | null };
12
+ }
@@ -0,0 +1,11 @@
1
+ import type { Mock } from "vitest";
2
+
3
+ export const testCallbacksExecution = (callbacks: Array<Mock>) => {
4
+ callbacks.forEach((spyFn, index) => {
5
+ const nextSpy = callbacks[index + 1];
6
+ if (nextSpy) {
7
+ expect(spyFn).toHaveBeenCalledBefore(nextSpy);
8
+ }
9
+ expect(spyFn).toHaveBeenCalledTimes(1);
10
+ });
11
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./callbacks.shared";
2
+ export * from "./progress.shared";
@@ -0,0 +1,40 @@
1
+ import type { RequestInstance } from "request";
2
+ import { getProgressValue, getRequestEta } from "request";
3
+
4
+ export const testProgressSpy = (props: {
5
+ spy: () => void;
6
+ requestId: string;
7
+ request: RequestInstance;
8
+ startTimestamp: number;
9
+ progressTimestamp?: number;
10
+ total?: number;
11
+ loaded?: number;
12
+ timeLeft?: number;
13
+ }) => {
14
+ const { spy, requestId, request, startTimestamp, total, loaded, timeLeft, progressTimestamp } = props;
15
+
16
+ const totalValue = total ?? 1;
17
+ const loadedValue = loaded ?? 0;
18
+
19
+ const estimatedTime = getRequestEta(new Date(startTimestamp), new Date(progressTimestamp || startTimestamp), {
20
+ total: totalValue,
21
+ loaded: loadedValue,
22
+ }).timeLeft;
23
+ const timeToSend = loadedValue ? estimatedTime : null;
24
+
25
+ expect(spy).toHaveBeenCalledTimes(1);
26
+ expect(spy).toHaveBeenCalledWith(
27
+ {
28
+ loaded: loadedValue,
29
+ progress: getProgressValue({ total: totalValue, loaded: loadedValue }),
30
+ sizeLeft: totalValue - loadedValue,
31
+ startTimestamp,
32
+ timeLeft: timeLeft ?? timeToSend,
33
+ total: totalValue,
34
+ },
35
+ {
36
+ request,
37
+ requestId,
38
+ },
39
+ );
40
+ };
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "module": "esnext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["dom", "dom.iterable", "ESNext"],
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "skipLibCheck": true,
10
+ "esModuleInterop": true,
11
+ "allowSyntheticDefaultImports": true,
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "noUnusedLocals": false,
15
+ "noUnusedParameters": false,
16
+ "types": ["vitest/globals", "jest-extended", "@types/node"],
17
+ "baseUrl": "../src",
18
+ "paths": {
19
+ "@hyper-fetch/graphql": ["../src"]
20
+ }
21
+ },
22
+ "include": ["**/*.ts", "**/*.tsx", "../../../global.d.ts", "../../../vitest.d.ts"]
23
+ }
@@ -0,0 +1,10 @@
1
+ /// <reference types="vitest/globals" />
2
+ import { AbortController } from "abortcontroller-polyfill/dist/cjs-ponyfill";
3
+ import * as matchers from "jest-extended";
4
+ import { expect } from "vitest";
5
+
6
+ expect.extend(matchers);
7
+
8
+ if (!globalThis.AbortController) {
9
+ globalThis.AbortController = AbortController as any;
10
+ }
@@ -0,0 +1,4 @@
1
+ import { GraphQlExtraType } from './adapter.types';
2
+ export declare const defaultTimeout: number;
3
+ export declare const gqlExtra: GraphQlExtraType;
4
+ //# sourceMappingURL=adapter.constants.d.ts.map