Skip to content

Commit 1ef9476

Browse files
committed
Version 0.3.0
1 parent ad5e435 commit 1ef9476

File tree

3 files changed

+49
-37
lines changed

3 files changed

+49
-37
lines changed

README.md

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# deno_mock_fetch
22

3-
An _extremely_ simple way to mock `window.fetch`.
3+
An _extremely_ simple way to mock `globalThis.fetch`.
44

55
[Read the documentation][docs], or see "Usage" below.
66

@@ -14,9 +14,9 @@ Import the library and install the mock. Any fetches after calling `install()`
1414
will throw an error if you haven't explicitly added a mock for that route.
1515

1616
```typescript
17-
import * as mf from "https://deno.land/x/mock_fetch@0.2.0/mod.ts";
17+
import * as mf from "https://deno.land/x/mock_fetch@0.3.0/mod.ts";
1818

19-
// Replaces window.fetch with the mocked copy
19+
// Replaces globalThis.fetch with the mocked copy
2020
mf.install();
2121
```
2222

@@ -28,16 +28,16 @@ Call `mock` with a route (optionally starting with a method specifier, eg.
2828
`DELETE@`) and a function (can be async). Whenever that route is fetched, the
2929
function will be executed and the response will be returned.
3030

31-
The route uses [path-to-regexp], which allows you to use wildcard parameters.
31+
The route uses [URLPattern], which allows you to match patterns and wildcards.
3232

3333
**Only the path name will be used to match a handler**, so you can use literally
3434
anything for the host when fetching.
3535

36-
[path-to-regexp]: https://github.com/pillarjs/path-to-regexp#parameters
36+
[URLPattern]: https://github.com/WICG/urlpattern/blob/main/explainer.md#web-apis
3737

3838
```typescript
39-
mf.mock("GET@/api/hello/:name", (_req, match) => {
40-
return new Response(`Hello, ${match.params["name"]}!`, {
39+
mf.mock("GET@/api/hello/:name", (_req, params) => {
40+
return new Response(`Hello, ${params["name"]}!`, {
4141
status: 200,
4242
});
4343
});
@@ -55,7 +55,8 @@ with `reset`. Once the handler has been removed, that route will go back to
5555
throwing.
5656

5757
```typescript
58-
mf.remove("GET@/api/hello/:name"); // OR: mf.reset()
58+
mf.remove("GET@/api/hello/:name");
59+
// OR: mf.reset()
5960

6061
await fetch("https://example.com/api/hello/world");
6162
// UnhandledRouteError: GET /api/hello/world (0 routes have handlers)
@@ -102,12 +103,16 @@ myKy.put("blog/posts", {
102103

103104
You can destructure it, too.
104105

106+
```typescript
107+
const { fetch, mock, remove, reset} = mf.sandbox();
108+
```
109+
105110
<br>
106111

107112
## Credits
108113

109114
**[@eliassjogreen]**'s tiny router ([source][router]) does the bulk of the work.
110-
It's general purpose, but works great for Deno Deploy.
115+
It's general-purpose, but works great for Deno Deploy.
111116

112117
[@eliassjogreen]: https://github.com/eliassjogreen
113-
[router]: https://crux.land/router@0.0.4
118+
[router]: https://crux.land/router@0.0.5

mod.ts

Lines changed: 32 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
import { MatchHandler, router, Routes } from "https://crux.land/router@0.0.4";
1+
import { MatchHandler, router, Routes } from "https://crux.land/router@0.0.5";
2+
3+
export type { MatchHandler };
24

35
class UnhandledRouteError extends Error {
46
routes: Routes;
@@ -9,10 +11,12 @@ class UnhandledRouteError extends Error {
911
const method = request.method;
1012
const reqPath = new URL(request.url).pathname;
1113
const routesNumber = Object.entries(routes).length;
12-
const routePlural = routesNumber === 1 ? "route" : "routes";
14+
const routePlural = routesNumber === 1
15+
? "route has a handler"
16+
: "routes have handlers";
1317

1418
// deno-fmt-ignore
15-
super(`${method} ${reqPath} (${routesNumber} ${routePlural} have handlers)`);
19+
super(`${method} ${reqPath} (${routesNumber} ${routePlural})`);
1620

1721
this.name = this.constructor.name;
1822
if (Error.captureStackTrace) {
@@ -24,25 +28,24 @@ class UnhandledRouteError extends Error {
2428
}
2529
}
2630

27-
type MockFetch = {
31+
export interface MockFetch {
2832
fetch: typeof globalThis.fetch;
2933
mock: (route: string, handler: MatchHandler) => void;
3034
remove: (route: string) => void;
3135
reset: () => void;
32-
};
36+
}
3337

3438
/**
35-
* Create a stateful version of the global functions that do not contain
36-
* any global state.
37-
*
38-
* The returned object can be destructured.
39-
*
40-
* ```
41-
* const { fetch, mock, remove, reset } = sandbox()
42-
* ```
43-
*/
39+
* Get a set of functions that do not share any state with the globals.
40+
*
41+
* The returned object can be destructured.
42+
*
43+
* ```
44+
* const { fetch, mock, remove, reset } = sandbox()
45+
* ```
46+
*/
4447
export function sandbox(): MockFetch {
45-
const routeStore: Map<string, MatchHandler> = new Map();
48+
const routeStore = new Map<string, MatchHandler>();
4649

4750
async function fetch(
4851
input: string | Request | URL,
@@ -99,14 +102,15 @@ export const mockedFetch = globalMockFetch.fetch;
99102
/**
100103
* Mock a new route, or override an existing handler.
101104
*
102-
* The route uses path-to-regexp syntax, with the additional extension of
103-
* (optional) method routing (prefix with METHOD@, eg. `POST@/user/:id`).
105+
* The route uses URLPattern syntax, with the additional extension of
106+
* (optional) method routing by prefixing with the method,
107+
* eg. `"POST@/user/:id"`.
104108
*
105-
* The handler function can either be a function or an async function.
109+
* The handler function may be asynchronous.
106110
*
107111
* ```
108-
* mock("GET@/users/:id", async (_req, match) => {
109-
* const id = parseInt(match.params["id"]);
112+
* mock("GET@/users/:id", async (_req, params) => {
113+
* const id = parseInt(params["id"]);
110114
* const data = await magicallyGetMyUserData(id);
111115
* return new Response(JSON.stringify(data));
112116
* })
@@ -123,17 +127,20 @@ export const reset = globalMockFetch.reset;
123127
// Store the original fetch so it can be restored later
124128
const originalFetch = globalThis.fetch;
125129

130+
// The functions below are `const` for consistency.
131+
126132
/**
127-
* Replace `window.fetch` with a mock that routes requests to a matching handler.
133+
* Replace `globalThis.fetch` with `mockedFetch` (or another function that
134+
* matches the `fetch` signature)
128135
*
129-
* To reset `window.fetch`, call `uninstall()`.
136+
* To restore the original `globalThis.fetch`, call `uninstall()`.
130137
*/
131-
export const install = () => {
132-
globalThis.fetch = mockedFetch;
138+
export const install = (replacement?: typeof fetch) => {
139+
globalThis.fetch = replacement ?? mockedFetch;
133140
};
134141

135142
/**
136-
* Restore `window.fetch` to what it was before `install()` was called.
143+
* Restore `globalThis.fetch` to what it was before this library was imported.
137144
*/
138145
export const uninstall = () => {
139146
globalThis.fetch = originalFetch;

test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
assertEquals,
33
assertNotEquals,
44
assertThrowsAsync,
5-
} from "https://deno.land/std@0.101.0/testing/asserts.ts";
5+
} from "https://deno.land/std@0.113.0/testing/asserts.ts";
66
import * as mf from "./mod.ts";
77

88
Deno.test({
@@ -81,7 +81,7 @@ Deno.test({
8181
mf.install();
8282

8383
mf.mock("DELETE@/lights/:id", (_req, match) => {
84-
assertEquals(match.params["id"], "2");
84+
assertEquals(match["id"], "2");
8585
return new Response();
8686
});
8787

0 commit comments

Comments
 (0)