Skip to content

Commit

Permalink
chore: additional tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tvervest authored and blurrah committed Jul 22, 2024
1 parent 65db905 commit bcad59d
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 14 deletions.
29 changes: 27 additions & 2 deletions src/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ describe("gqlClientFetch", () => {
headers: {
"Content-Type": "application/json",
},
signal: expect.any(AbortSignal),
}
);
});
Expand All @@ -84,7 +85,7 @@ describe("gqlClientFetch", () => {
"Content-Type": "application/json",
},
signal: expect.any(AbortSignal),
},
}
);
});
it("should perform a mutation", async () => {
Expand Down Expand Up @@ -161,7 +162,7 @@ describe("gqlClientFetch", () => {

it("should use the provided timeout duration", async () => {
const fetcher = initClientFetcher("https://localhost/graphql", {
timeout: 1,
defaultTimeout: 1,
});
const timeoutSpy = vi.spyOn(AbortSignal, "timeout");
fetchMock.mockResponse(responseString);
Expand All @@ -175,4 +176,28 @@ describe("gqlClientFetch", () => {
// It should not try to POST the query if the persisted query cannot be parsed
expect(fetchMock).toHaveBeenCalledTimes(1);
});

it("should use the provided signal", async () => {
const fetcher = initClientFetcher("https://localhost/graphql");
fetchMock.mockResponse(responseString);

const controller = new AbortController();
await fetcher(
query,
{
myVar: "baz",
},
controller.signal
);

expect(fetchMock).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
signal: controller.signal,
})
);

// It should not try to POST the query if the persisted query cannot be parsed
expect(fetchMock).toHaveBeenCalledTimes(1);
});
});
9 changes: 5 additions & 4 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ type Options = {
persistedQueries?: boolean;

/**
* Sets the timeout duration in ms after which a request will throw a timeout error
* Sets the default timeout duration in ms after which a request will throw a timeout error
*
* @default 30000
*/
timeout?: number;
defaultTimeout?: number;
};

export type ClientFetcher = <TResponse, TVariables>(
Expand All @@ -35,7 +35,7 @@ export type ClientFetcher = <TResponse, TVariables>(
export const initClientFetcher =
(
endpoint: string,
{ persistedQueries = false, timeout = 30000 }: Options = {}
{ persistedQueries = false, defaultTimeout = 30000 }: Options = {}
): ClientFetcher =>
/**
* Executes a GraphQL query post request on the client.
Expand All @@ -46,7 +46,7 @@ export const initClientFetcher =
async <TResponse, TVariables>(
astNode: DocumentTypeDecoration<TResponse, TVariables>,
variables?: TVariables,
signal: AbortSignal = AbortSignal.timeout(timeout)
signal: AbortSignal = AbortSignal.timeout(defaultTimeout)
): Promise<GqlResponse<TResponse>> => {
const query = astNode.toString();

Expand Down Expand Up @@ -94,6 +94,7 @@ export const initClientFetcher =
method: "POST",
body: JSON.stringify({ query, variables, extensions }),
credentials: "include",
signal,
})
);
}
Expand Down
24 changes: 23 additions & 1 deletion src/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ describe("gqlServerFetch", () => {
},
cache: "force-cache",
next: { revalidate: 900 },
signal: expect.any(AbortSignal),
}
);
});
Expand Down Expand Up @@ -119,6 +120,7 @@ describe("gqlServerFetch", () => {
},
cache: "no-store",
next: { revalidate: undefined },
signal: expect.any(AbortSignal),
}
);
});
Expand Down Expand Up @@ -192,7 +194,7 @@ describe("gqlServerFetch", () => {
it("should use the provided timeout duration", async () => {
const timeoutSpy = vi.spyOn(AbortSignal, "timeout");
const gqlServerFetch = initServerFetcher("https://localhost/graphql", {
timeout: 1,
defaultTimeout: 1,
});
fetchMock.mockResponse(successResponse);

Expand All @@ -203,4 +205,24 @@ describe("gqlServerFetch", () => {
// It should not try to POST the query if the persisted query cannot be parsed
expect(fetchMock).toHaveBeenCalledTimes(1);
});

it("should use the provided signal", async () => {
const gqlServerFetch = initServerFetcher("https://localhost/graphql", {
defaultTimeout: 1,
});
fetchMock.mockResponse(successResponse);

const controller = new AbortController();
await gqlServerFetch(query, { myVar: "baz" }, {}, controller.signal);

expect(fetchMock).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
signal: controller.signal,
})
);

// It should not try to POST the query if the persisted query cannot be parsed
expect(fetchMock).toHaveBeenCalledTimes(1);
});
});
18 changes: 11 additions & 7 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ type Options = {
dangerouslyDisableCache?: boolean;

/**
* Sets the timeout duration in ms after which a request will throw a timeout error
* Sets the default timeout duration in ms after which a request will throw a timeout error
*
* @default 30000
*/
timeout?: number;
defaultTimeout?: number;
};

type CacheOptions = {
Expand All @@ -40,13 +40,13 @@ const tracer = trace.getTracer(
export const initServerFetcher =
(
url: string,
{ dangerouslyDisableCache = false, timeout = 30000 }: Options = {}
{ dangerouslyDisableCache = false, defaultTimeout = 30000 }: Options = {}
) =>
async <TResponse, TVariables>(
astNode: DocumentTypeDecoration<TResponse, TVariables>,
variables: TVariables,
{ cache, next = {} }: CacheOptions,
signal: AbortSignal = AbortSignal.timeout(timeout)
signal: AbortSignal = AbortSignal.timeout(defaultTimeout)
): Promise<GqlResponse<TResponse>> => {
const query = astNode.toString();
const operationName = extractOperationName(query) || "(GraphQL)";
Expand Down Expand Up @@ -93,15 +93,17 @@ export const initServerFetcher =
let response = await gqlPersistedQuery(
url,
getQueryString(operationName, variables, extensions),
{ cache, next }
{ cache, next },
signal
);

if (response.errors?.[0]?.message === "PersistedQueryNotFound") {
// If the cached query doesn't exist, fall back to POST request and let the server cache it.
response = await gqlPost(
url,
JSON.stringify({ operationName, query, variables, extensions }),
{ cache, next }
{ cache, next },
signal
);
}

Expand Down Expand Up @@ -138,13 +140,15 @@ const gqlPost = async (
const gqlPersistedQuery = async (
url: string,
queryString: URLSearchParams,
{ cache, next }: CacheOptions
{ cache, next }: CacheOptions,
signal: AbortSignal = AbortSignal.timeout(30000)
) => {
const response = await fetch(`${url}?${queryString}`, {
method: "GET",
headers: defaultHeaders,
cache,
next,
signal,
});

return parseResponse(response);
Expand Down

0 comments on commit bcad59d

Please sign in to comment.