Skip to content

Commit 3ec126c

Browse files
committed
fetch browser unit tests
1 parent 4325968 commit 3ec126c

File tree

3 files changed

+42
-22
lines changed

3 files changed

+42
-22
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 & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
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";
7+
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+
});
614

715
// TODO(vitest): fix this test.
8-
describe.skip(FetchHttpHandler.name, () => {
16+
describe(FetchHttpHandler.name, () => {
917
interface MockHttpRequestOptions {
1018
method?: string;
1119
body?: any;
@@ -19,52 +27,53 @@ describe.skip(FetchHttpHandler.name, () => {
1927
new HttpRequest({ hostname: "example.com", ...options });
2028

2129
describe("fetch", () => {
30+
beforeAll(() => {
31+
keepAliveSupport.supported = true;
32+
});
33+
2234
afterEach(() => {
2335
vi.clearAllMocks();
2436
});
2537

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

3041
const mockHttpRequest = getMockHttpRequest({});
3142
await fetchHttpHandler.handle(mockHttpRequest);
3243

3344
const expectedUrl = `${mockHttpRequest.protocol}//${mockHttpRequest.hostname}/`;
34-
const requestArgs = winReqSpy.mock.calls[0][0];
45+
const requestArgs = vi.mocked(createRequest).mock.calls[0];
46+
3547
expect(requestArgs[0]).toEqual(expectedUrl);
36-
expect(requestArgs[1].method).toEqual(mockHttpRequest.method);
37-
expect(requestArgs[1].keepalive).toEqual(false);
48+
expect(requestArgs[1]!.method).toEqual(mockHttpRequest.method);
49+
expect(requestArgs[1]!.keepalive).toEqual(false);
3850
});
3951

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

4556
const mockHttpRequest = getMockHttpRequest({ method, body: "test" });
4657
await fetchHttpHandler.handle(mockHttpRequest);
4758

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

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

5868
const mockHttpRequest = getMockHttpRequest({});
5969
await fetchHttpHandler.handle(mockHttpRequest);
6070

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

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

6978
const query = { foo: "bar" };
7079
const fragment = "test";
@@ -74,22 +83,23 @@ describe.skip(FetchHttpHandler.name, () => {
7483
const expectedUrl = `${mockHttpRequest.protocol}//${mockHttpRequest.hostname}/?${Object.entries(query)
7584
.map(([key, val]) => `${key}=${val}`)
7685
.join("&")}#${fragment}`;
77-
const requestArgs = winReqSpy.mock.calls[0][0];
86+
const requestArgs = vi.mocked(createRequest).mock.calls[0];
7887
expect(requestArgs[0]).toEqual(expectedUrl);
7988
});
8089

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

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

90100
const mockAuth = `${mockHttpRequest.username}:${mockHttpRequest.password}`;
91101
const expectedUrl = `${mockHttpRequest.protocol}//${mockAuth}@${mockHttpRequest.hostname}/`;
92-
const requestArgs = winReqSpy.mock.calls[0][0];
102+
const requestArgs = vi.mocked(createRequest).mock.calls[0];
93103
expect(requestArgs[0]).toEqual(expectedUrl);
94104
});
95105
});

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)