Skip to content

Commit 3d0d28c

Browse files
authored
Updates to createStaticHandler for Remix consumption (#9482)
* Updates to createStaticHandler for Remix consumption * unit tests * add changeset * one more test for HEAD in queryRoute * add tests
1 parent 80d5844 commit 3d0d28c

File tree

3 files changed

+213
-66
lines changed

3 files changed

+213
-66
lines changed

.changeset/funny-shirts-admire.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@remix-run/router": patch
3+
---
4+
5+
Changes to statis handler for incorporating into Remix"

packages/router/__tests__/router-test.ts

Lines changed: 159 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,6 +2144,31 @@ describe("a router", () => {
21442144
]);
21452145
});
21462146

2147+
it("matches root pathless route", () => {
2148+
let t = setup({
2149+
routes: [{ id: "root", children: [{ path: "foo" }] }],
2150+
});
2151+
2152+
t.navigate("/not-found");
2153+
expect(t.router.state.errors).toEqual({
2154+
root: {
2155+
status: 404,
2156+
statusText: "Not Found",
2157+
data: null,
2158+
},
2159+
});
2160+
expect(t.router.state.matches).toMatchObject([
2161+
{
2162+
params: {},
2163+
pathname: "",
2164+
route: {
2165+
id: "root",
2166+
children: expect.any(Array),
2167+
},
2168+
},
2169+
]);
2170+
});
2171+
21472172
it("clears prior loader/action data", async () => {
21482173
let t = initializeTmTest();
21492174
expect(t.router.state.loaderData).toEqual({
@@ -3128,11 +3153,7 @@ describe("a router", () => {
31283153
formData: createFormData({ gosh: "dang" }),
31293154
});
31303155
expect(t.router.state.errors).toEqual({
3131-
child: new ErrorResponse(
3132-
405,
3133-
"Method Not Allowed",
3134-
"No action found for [/child]"
3135-
),
3156+
child: new ErrorResponse(405, "Method Not Allowed", ""),
31363157
});
31373158
expect(console.warn).toHaveBeenCalled();
31383159
spy.mockReset();
@@ -3185,11 +3206,7 @@ describe("a router", () => {
31853206
});
31863207
expect(t.router.state.actionData).toBe(null);
31873208
expect(t.router.state.errors).toEqual({
3188-
grandchild: new ErrorResponse(
3189-
405,
3190-
"Method Not Allowed",
3191-
"No action found for [/child/grandchild]"
3192-
),
3209+
grandchild: new ErrorResponse(405, "Method Not Allowed", ""),
31933210
});
31943211
});
31953212
});
@@ -6667,11 +6684,7 @@ describe("a router", () => {
66676684
});
66686685
expect(A.fetcher).toBe(IDLE_FETCHER);
66696686
expect(t.router.state.errors).toEqual({
6670-
root: new ErrorResponse(
6671-
405,
6672-
"Method Not Allowed",
6673-
"No action found for [/]"
6674-
),
6687+
root: new ErrorResponse(405, "Method Not Allowed", ""),
66756688
});
66766689
});
66776690

@@ -9906,6 +9919,23 @@ describe("a router", () => {
99069919
});
99079920
});
99089921

9922+
it("should support document load navigations with HEAD requests", async () => {
9923+
let { query } = createStaticHandler(SSR_ROUTES);
9924+
let context = await query(
9925+
createRequest("/parent/child", { method: "HEAD" })
9926+
);
9927+
expect(context).toMatchObject({
9928+
actionData: null,
9929+
loaderData: {
9930+
parent: "PARENT LOADER",
9931+
child: "CHILD LOADER",
9932+
},
9933+
errors: null,
9934+
location: { pathname: "/parent/child" },
9935+
matches: [{ route: { id: "parent" } }, { route: { id: "child" } }],
9936+
});
9937+
});
9938+
99099939
it("should support document load navigations returning responses", async () => {
99109940
let { query } = createStaticHandler(SSR_ROUTES);
99119941
let context = await query(createRequest("/parent/json"));
@@ -9962,6 +9992,39 @@ describe("a router", () => {
99629992
});
99639993
});
99649994

9995+
it("should support alternative submission methods", async () => {
9996+
let { query } = createStaticHandler(SSR_ROUTES);
9997+
let context;
9998+
9999+
let expected = {
10000+
actionData: {
10001+
child: "CHILD ACTION",
10002+
},
10003+
loaderData: {
10004+
parent: "PARENT LOADER",
10005+
child: "CHILD LOADER",
10006+
},
10007+
errors: null,
10008+
location: { pathname: "/parent/child" },
10009+
matches: [{ route: { id: "parent" } }, { route: { id: "child" } }],
10010+
};
10011+
10012+
context = await query(
10013+
createSubmitRequest("/parent/child", { method: "PUT" })
10014+
);
10015+
expect(context).toMatchObject(expected);
10016+
10017+
context = await query(
10018+
createSubmitRequest("/parent/child", { method: "PATCH" })
10019+
);
10020+
expect(context).toMatchObject(expected);
10021+
10022+
context = await query(
10023+
createSubmitRequest("/parent/child", { method: "DELETE" })
10024+
);
10025+
expect(context).toMatchObject(expected);
10026+
});
10027+
996510028
it("should support document submit navigations returning responses", async () => {
996610029
let { query } = createStaticHandler(SSR_ROUTES);
996710030
let context = await query(createSubmitRequest("/parent/json"));
@@ -10202,20 +10265,6 @@ describe("a router", () => {
1020210265
expect(e).toMatchInlineSnapshot(`[Error: query() call aborted]`);
1020310266
});
1020410267

10205-
it("should not support HEAD requests", async () => {
10206-
let { query } = createStaticHandler(SSR_ROUTES);
10207-
let request = createRequest("/", { method: "head" });
10208-
let e;
10209-
try {
10210-
await query(request);
10211-
} catch (_e) {
10212-
e = _e;
10213-
}
10214-
expect(e).toMatchInlineSnapshot(
10215-
`[Error: query()/queryRoute() do not support HEAD requests]`
10216-
);
10217-
});
10218-
1021910268
it("should require a signal on the request", async () => {
1022010269
let { query } = createStaticHandler(SSR_ROUTES);
1022110270
let request = createRequest("/", { signal: undefined });
@@ -10246,7 +10295,30 @@ describe("a router", () => {
1024610295
root: {
1024710296
status: 405,
1024810297
statusText: "Method Not Allowed",
10249-
data: "No action found for [/]",
10298+
data: "",
10299+
},
10300+
},
10301+
matches: [{ route: { id: "root" } }],
10302+
});
10303+
});
10304+
10305+
it("should handle unsupported methods with a 405 error", async () => {
10306+
let { query } = createStaticHandler([
10307+
{
10308+
id: "root",
10309+
path: "/",
10310+
},
10311+
]);
10312+
let request = createRequest("/", { method: "OPTIONS" });
10313+
let context = await query(request);
10314+
expect(context).toMatchObject({
10315+
actionData: null,
10316+
loaderData: {},
10317+
errors: {
10318+
root: {
10319+
status: 405,
10320+
statusText: "Method Not Allowed",
10321+
data: null,
1025010322
},
1025110323
},
1025210324
matches: [{ route: { id: "root" } }],
@@ -10632,6 +10704,14 @@ describe("a router", () => {
1063210704
expect(data).toBe("CHILD LOADER");
1063310705
});
1063410706

10707+
it("should support HEAD requests", async () => {
10708+
let { queryRoute } = createStaticHandler(SSR_ROUTES);
10709+
let data = await queryRoute(
10710+
createRequest("/parent", { method: "HEAD" })
10711+
);
10712+
expect(data).toBe("PARENT LOADER");
10713+
});
10714+
1063510715
it("should support singular route load navigations (primitives)", async () => {
1063610716
let { queryRoute } = createStaticHandler(SSR_ROUTES);
1063710717
let data;
@@ -10800,6 +10880,29 @@ describe("a router", () => {
1080010880
expect(data).toBe("");
1080110881
});
1080210882

10883+
it("should support alternative submission methods", async () => {
10884+
let { queryRoute } = createStaticHandler(SSR_ROUTES);
10885+
let data;
10886+
10887+
data = await queryRoute(
10888+
createSubmitRequest("/parent", { method: "PUT" }),
10889+
"parent"
10890+
);
10891+
expect(data).toBe("PARENT ACTION");
10892+
10893+
data = await queryRoute(
10894+
createSubmitRequest("/parent", { method: "PATCH" }),
10895+
"parent"
10896+
);
10897+
expect(data).toBe("PARENT ACTION");
10898+
10899+
data = await queryRoute(
10900+
createSubmitRequest("/parent", { method: "DELETE" }),
10901+
"parent"
10902+
);
10903+
expect(data).toBe("PARENT ACTION");
10904+
});
10905+
1080310906
it("should support singular route submit navigations (Responses)", async () => {
1080410907
/* eslint-disable jest/no-conditional-expect */
1080510908
let T = setupFlexRouteTest();
@@ -11042,20 +11145,6 @@ describe("a router", () => {
1104211145
expect(e).toMatchInlineSnapshot(`[Error: queryRoute() call aborted]`);
1104311146
});
1104411147

11045-
it("should not support HEAD requests", async () => {
11046-
let { queryRoute } = createStaticHandler(SSR_ROUTES);
11047-
let request = createRequest("/", { method: "head" });
11048-
let e;
11049-
try {
11050-
await queryRoute(request, "index");
11051-
} catch (_e) {
11052-
e = _e;
11053-
}
11054-
expect(e).toMatchInlineSnapshot(
11055-
`[Error: query()/queryRoute() do not support HEAD requests]`
11056-
);
11057-
});
11058-
1105911148
it("should require a signal on the request", async () => {
1106011149
let { queryRoute } = createStaticHandler(SSR_ROUTES);
1106111150
let request = createRequest("/", { signal: undefined });
@@ -11139,7 +11228,32 @@ describe("a router", () => {
1113911228
expect(data.status).toBe(405);
1114011229
expect(data.statusText).toBe("Method Not Allowed");
1114111230
expect(data.headers.get("X-Remix-Router-Error")).toBe("yes");
11142-
expect(await data.text()).toBe("No action found for [/]");
11231+
expect(await data.text()).toBe("");
11232+
}
11233+
/* eslint-enable jest/no-conditional-expect */
11234+
});
11235+
11236+
it("should handle unsupported methods with a 405 Response", async () => {
11237+
/* eslint-disable jest/no-conditional-expect */
11238+
let { queryRoute } = createStaticHandler([
11239+
{
11240+
id: "root",
11241+
path: "/",
11242+
},
11243+
]);
11244+
11245+
try {
11246+
await queryRoute(
11247+
createSubmitRequest("/", { method: "OPTIONS" }),
11248+
"root"
11249+
);
11250+
expect(false).toBe(true);
11251+
} catch (data) {
11252+
expect(data instanceof Response).toBe(true);
11253+
expect(data.status).toBe(405);
11254+
expect(data.statusText).toBe("Method Not Allowed");
11255+
expect(data.headers.get("X-Remix-Router-Error")).toBe("yes");
11256+
expect(await data.text()).toBe("");
1114311257
}
1114411258
/* eslint-enable jest/no-conditional-expect */
1114511259
});

0 commit comments

Comments
 (0)