@@ -935,6 +935,115 @@ implementations.forEach((implementation) => {
935
935
} ) ;
936
936
} ) ;
937
937
938
+ test ( "Handles error responses from resource routes missing loaders/actions" , async ( {
939
+ page,
940
+ request,
941
+ } ) => {
942
+ let port = await getPort ( ) ;
943
+ stop = await setupRscTest ( {
944
+ implementation,
945
+ port,
946
+ files : {
947
+ "src/routes.ts" : js `
948
+ import type { unstable_RSCRouteConfig as RSCRouteConfig } from "react-router";
949
+
950
+ export const routes = [
951
+ {
952
+ id: "root",
953
+ path: "",
954
+ lazy: () => import("./routes/root"),
955
+ children: [
956
+ {
957
+ id: "home",
958
+ index: true,
959
+ lazy: () => import("./routes/home"),
960
+ },
961
+ ],
962
+ },
963
+ {
964
+ id: "no-loader-resource",
965
+ path: "no-loader-resource",
966
+ lazy: () => import("./routes/no-loader-resource"),
967
+ },
968
+ {
969
+ id: "no-action-resource",
970
+ path: "no-action-resource",
971
+ lazy: () => import("./routes/no-action-resource"),
972
+ },
973
+ ] satisfies RSCRouteConfig;
974
+ ` ,
975
+ "src/routes/root.tsx" : js `
976
+ import { Outlet } from "react-router";
977
+ export default function RootRoute() {
978
+ return (
979
+ <div>
980
+ <h1>Root Route</h1>
981
+ <Outlet />
982
+ </div>
983
+ );
984
+ }
985
+ ` ,
986
+ "src/routes/home.tsx" : js `
987
+ export default function HomeRoute() {
988
+ return (
989
+ <div>
990
+ <h2 data-home>Home Route</h2>
991
+ </div>
992
+ );
993
+ }
994
+ ` ,
995
+ "src/routes/no-loader-resource.tsx" : js `
996
+ // This resource route has no loader, so GET requests should fail
997
+ export async function action() {
998
+ return { message: "no-loader-resource action works" };
999
+ }
1000
+ ` ,
1001
+ "src/routes/no-action-resource.tsx" : js `
1002
+ // This resource route has no action, so POST requests should fail
1003
+ export function loader() {
1004
+ return { message: "no-action-resource loader works" };
1005
+ }
1006
+ ` ,
1007
+ } ,
1008
+ } ) ;
1009
+
1010
+ const getResponse = await request . get (
1011
+ `http://localhost:${ port } /no-loader-resource`
1012
+ ) ;
1013
+ expect ( getResponse ?. status ( ) ) . toBe ( 400 ) ;
1014
+ expect ( await getResponse ?. text ( ) ) . toBe (
1015
+ 'Error: You made a GET request to "/no-loader-resource" but did not provide a `loader` for route "no-loader-resource", so there is no way to handle the request.'
1016
+ ) ;
1017
+
1018
+ const postResponse = await request . post (
1019
+ `http://localhost:${ port } /no-action-resource`
1020
+ ) ;
1021
+ expect ( postResponse ?. status ( ) ) . toBe ( 405 ) ;
1022
+ expect ( await postResponse ?. text ( ) ) . toBe (
1023
+ 'Error: You made a POST request to "/no-action-resource" but did not provide an `action` for route "no-action-resource", so there is no way to handle the request.'
1024
+ ) ;
1025
+
1026
+ const postWithActionResponse = await request . post (
1027
+ `http://localhost:${ port } /no-loader-resource`
1028
+ ) ;
1029
+ expect ( postWithActionResponse ?. status ( ) ) . toBe ( 200 ) ;
1030
+ expect ( await postWithActionResponse ?. json ( ) ) . toEqual ( {
1031
+ message : "no-loader-resource action works" ,
1032
+ } ) ;
1033
+
1034
+ const getWithLoaderResponse = await request . get (
1035
+ `http://localhost:${ port } /no-action-resource`
1036
+ ) ;
1037
+ expect ( getWithLoaderResponse ?. status ( ) ) . toBe ( 200 ) ;
1038
+ expect ( await getWithLoaderResponse ?. json ( ) ) . toEqual ( {
1039
+ message : "no-action-resource loader works" ,
1040
+ } ) ;
1041
+
1042
+ // Ensure this is using RSC
1043
+ await page . goto ( `http://localhost:${ port } /` ) ;
1044
+ validateRSCHtml ( await page . content ( ) ) ;
1045
+ } ) ;
1046
+
938
1047
test . describe ( "Server Actions" , ( ) => {
939
1048
test ( "Supports React Server Functions" , async ( { page } ) => {
940
1049
let port = await getPort ( ) ;
0 commit comments