Skip to content

Commit b978453

Browse files
committed
fetch browser unit tests
1 parent 4325968 commit b978453

File tree

3 files changed

+42
-23
lines changed

3 files changed

+42
-23
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { AdditionalRequestParameters } from "./fetch-http-handler";
2+
3+
/**
4+
* @internal
5+
* For mocking/interception.
6+
*/
7+
export function createRequest(url: string, requestOptions?: RequestInit & AdditionalRequestParameters) {
8+
return new Request(url, requestOptions);
9+
}

packages/fetch-http-handler/src/fetch-http-handler.browser.spec.ts

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { HttpRequest } from "@smithy/protocol-http";
22
import { QueryParameterBag } from "@smithy/types";
3-
import { afterEach, describe, expect, test as it, vi } from "vitest";
3+
import { afterEach, beforeAll, describe, expect, test as it, vi } from "vitest";
44

5-
import { FetchHttpHandler } from "./fetch-http-handler";
5+
import { createRequest } from "./create-request";
6+
import { FetchHttpHandler, keepAliveSupport } from "./fetch-http-handler";
67

7-
// TODO(vitest): fix this test.
8-
describe.skip(FetchHttpHandler.name, () => {
8+
vi.mock("./create-request", async () => {
9+
const actual: any = await vi.importActual("./create-request");
10+
return {
11+
createRequest: vi.fn().mockImplementation(actual.createRequest),
12+
};
13+
});
14+
15+
describe(FetchHttpHandler.name, () => {
916
interface MockHttpRequestOptions {
1017
method?: string;
1118
body?: any;
@@ -19,52 +26,53 @@ describe.skip(FetchHttpHandler.name, () => {
1926
new HttpRequest({ hostname: "example.com", ...options });
2027

2128
describe("fetch", () => {
29+
beforeAll(() => {
30+
keepAliveSupport.supported = true;
31+
});
32+
2233
afterEach(() => {
2334
vi.clearAllMocks();
2435
});
2536

2637
it("sends basic fetch request", async () => {
2738
const fetchHttpHandler = new FetchHttpHandler();
28-
const winReqSpy = vi.spyOn(window, "Request");
2939

3040
const mockHttpRequest = getMockHttpRequest({});
3141
await fetchHttpHandler.handle(mockHttpRequest);
3242

3343
const expectedUrl = `${mockHttpRequest.protocol}//${mockHttpRequest.hostname}/`;
34-
const requestArgs = winReqSpy.mock.calls[0][0];
44+
const requestArgs = vi.mocked(createRequest).mock.calls[0];
45+
3546
expect(requestArgs[0]).toEqual(expectedUrl);
36-
expect(requestArgs[1].method).toEqual(mockHttpRequest.method);
37-
expect(requestArgs[1].keepalive).toEqual(false);
47+
expect(requestArgs[1]!.method).toEqual(mockHttpRequest.method);
48+
expect(requestArgs[1]!.keepalive).toEqual(false);
3849
});
3950

4051
for (const method of ["GET", "HEAD"]) {
4152
it(`sets body to undefined when method: '${method}'`, async () => {
4253
const fetchHttpHandler = new FetchHttpHandler();
43-
const winReqSpy = vi.spyOn(window, "Request");
4454

4555
const mockHttpRequest = getMockHttpRequest({ method, body: "test" });
4656
await fetchHttpHandler.handle(mockHttpRequest);
4757

48-
const requestArgs = winReqSpy.mock.calls[0][0];
49-
expect(requestArgs[1].method).toEqual(mockHttpRequest.method);
50-
expect(requestArgs[1].body).toEqual(undefined);
58+
const requestArgs = vi.mocked(createRequest).mock.calls[0];
59+
expect(requestArgs[1]!.method).toEqual(mockHttpRequest.method);
60+
expect(requestArgs[1]!.body).toEqual(undefined);
5161
});
5262
}
5363

5464
it(`sets keepalive to true if explicitly requested`, async () => {
5565
const fetchHttpHandler = new FetchHttpHandler({ keepAlive: true });
56-
const winReqSpy = vi.spyOn(window, "Request");
5766

5867
const mockHttpRequest = getMockHttpRequest({});
5968
await fetchHttpHandler.handle(mockHttpRequest);
6069

61-
const requestArgs = winReqSpy.mock.calls[0][0];
62-
expect(requestArgs[1].keepalive).toEqual(true);
70+
const requestArgs = vi.mocked(createRequest).mock.calls[0];
71+
expect(requestArgs[1]!.keepalive).toEqual(true);
6372
});
6473

6574
it(`builds querystring if provided`, async () => {
6675
const fetchHttpHandler = new FetchHttpHandler();
67-
const winReqSpy = vi.spyOn(window, "Request");
6876

6977
const query = { foo: "bar" };
7078
const fragment = "test";
@@ -74,22 +82,23 @@ describe.skip(FetchHttpHandler.name, () => {
7482
const expectedUrl = `${mockHttpRequest.protocol}//${mockHttpRequest.hostname}/?${Object.entries(query)
7583
.map(([key, val]) => `${key}=${val}`)
7684
.join("&")}#${fragment}`;
77-
const requestArgs = winReqSpy.mock.calls[0][0];
85+
const requestArgs = vi.mocked(createRequest).mock.calls[0];
7886
expect(requestArgs[0]).toEqual(expectedUrl);
7987
});
8088

8189
it(`sets auth if username/password are provided`, async () => {
8290
const fetchHttpHandler = new FetchHttpHandler();
83-
const winReqSpy = vi.spyOn(window, "Request");
8491

8592
const username = "foo";
8693
const password = "bar";
8794
const mockHttpRequest = getMockHttpRequest({ username, password });
88-
await fetchHttpHandler.handle(mockHttpRequest);
95+
await fetchHttpHandler.handle(mockHttpRequest).catch(error => {
96+
expect(String(error)).toContain("TypeError: Request cannot be constructed from a URL that includes credentials");
97+
})
8998

9099
const mockAuth = `${mockHttpRequest.username}:${mockHttpRequest.password}`;
91100
const expectedUrl = `${mockHttpRequest.protocol}//${mockAuth}@${mockHttpRequest.hostname}/`;
92-
const requestArgs = winReqSpy.mock.calls[0][0];
101+
const requestArgs = vi.mocked(createRequest).mock.calls[0];
93102
expect(requestArgs[0]).toEqual(expectedUrl);
94103
});
95104
});

packages/fetch-http-handler/src/fetch-http-handler.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { buildQueryString } from "@smithy/querystring-builder";
33
import type { FetchHttpHandlerOptions } from "@smithy/types";
44
import { HeaderBag, HttpHandlerOptions, Provider } from "@smithy/types";
55

6+
import { createRequest } from "./create-request";
67
import { requestTimeout } from "./request-timeout";
78

89
declare let AbortController: any;
@@ -22,7 +23,7 @@ export const keepAliveSupport = {
2223
/**
2324
* @internal
2425
*/
25-
type AdditionalRequestParameters = {
26+
export type AdditionalRequestParameters = {
2627
// This is required in Node.js when Request has a body, and does nothing in the browser.
2728
// Duplex: half means the request is fully transmitted before attempting to process the response.
2829
// As of writing this is the only accepted value in https://fetch.spec.whatwg.org/.
@@ -62,7 +63,7 @@ export class FetchHttpHandler implements HttpHandler<FetchHttpHandlerConfig> {
6263
}
6364
if (keepAliveSupport.supported === undefined) {
6465
keepAliveSupport.supported = Boolean(
65-
typeof Request !== "undefined" && "keepalive" in new Request("https://[::1]")
66+
typeof Request !== "undefined" && "keepalive" in createRequest("https://[::1]")
6667
);
6768
}
6869
}
@@ -139,7 +140,7 @@ export class FetchHttpHandler implements HttpHandler<FetchHttpHandlerConfig> {
139140

140141
let removeSignalEventListener = () => {};
141142

142-
const fetchRequest = new Request(url, requestOptions);
143+
const fetchRequest = createRequest(url, requestOptions);
143144
const raceOfPromises = [
144145
fetch(fetchRequest).then((response) => {
145146
const fetchHeaders: any = response.headers;

0 commit comments

Comments
 (0)