@@ -9726,7 +9726,10 @@ describe("a router", () => {
9726
9726
expect(A.loaders.foo.signal.aborted).toBe(false);
9727
9727
expect(t.router.state.navigation.state).toBe("idle");
9728
9728
expect(t.router.state.location.pathname).toBe("/foo");
9729
- expect(t.router.state.loaderData.foo).toBe("B");
9729
+ expect(t.router.state.loaderData).toEqual({
9730
+ root: "B root",
9731
+ foo: "B",
9732
+ });
9730
9733
9731
9734
await A.loaders.root.resolve("A root");
9732
9735
await A.loaders.foo.resolve("A");
@@ -10009,6 +10012,180 @@ describe("a router", () => {
10009
10012
expect(t.router.state.fetchers.get(key)?.data).toBeUndefined();
10010
10013
});
10011
10014
});
10015
+
10016
+ describe(`
10017
+ A) fetch POST /foo |--R
10018
+ B) nav GET /bar |---O
10019
+ `, () => {
10020
+ it("ignores submission redirect navigation if preceded by a normal navigation", async () => {
10021
+ let key = "key";
10022
+ let t = initializeTmTest();
10023
+ let A = await t.fetch("/foo", key, {
10024
+ formMethod: "post",
10025
+ formData: createFormData({ key: "value" }),
10026
+ });
10027
+ let B = await t.navigate("/bar");
10028
+
10029
+ // This redirect should be ignored
10030
+ await A.actions.foo.redirect("/baz");
10031
+ expect(t.router.state.fetchers.get(key)?.state).toBe("idle");
10032
+
10033
+ await B.loaders.root.resolve("ROOT*");
10034
+ await B.loaders.bar.resolve("BAR");
10035
+ expect(t.router.state).toMatchObject({
10036
+ navigation: IDLE_NAVIGATION,
10037
+ location: { pathname: "/bar" },
10038
+ loaderData: {
10039
+ root: "ROOT*",
10040
+ bar: "BAR",
10041
+ },
10042
+ });
10043
+ expect(t.router.state.fetchers.get(key)?.state).toBe("idle");
10044
+ expect(t.router.state.fetchers.get(key)?.data).toBeUndefined();
10045
+ });
10046
+ });
10047
+
10048
+ describe(`
10049
+ A) fetch GET /foo |--R
10050
+ B) nav GET /bar |---O
10051
+ `, () => {
10052
+ it("ignores loader redirect navigation if preceded by a normal navigation", async () => {
10053
+ let key = "key";
10054
+ let t = initializeTmTest();
10055
+
10056
+ // Start a fetch load and interrupt with a navigation
10057
+ let A = await t.fetch("/foo", key);
10058
+ let B = await t.navigate("/bar", undefined, ["foo"]);
10059
+
10060
+ // The fetcher loader redirect should be ignored
10061
+ await A.loaders.foo.redirect("/baz");
10062
+ expect(t.router.state.fetchers.get(key)?.state).toBe("loading");
10063
+
10064
+ // The navigation should trigger the fetcher to revalidate since it's
10065
+ // not yet "completed". If it returns data this time that should be
10066
+ // reflected
10067
+ await B.loaders.bar.resolve("BAR");
10068
+ await B.loaders.foo.resolve("FOO");
10069
+
10070
+ expect(t.router.state).toMatchObject({
10071
+ navigation: IDLE_NAVIGATION,
10072
+ location: { pathname: "/bar" },
10073
+ loaderData: {
10074
+ root: "ROOT",
10075
+ bar: "BAR",
10076
+ },
10077
+ });
10078
+ expect(t.router.state.fetchers.get(key)?.state).toBe("idle");
10079
+ expect(t.router.state.fetchers.get(key)?.data).toBe("FOO");
10080
+ });
10081
+
10082
+ it("processes second fetcher load redirect after interruption by normal navigation", async () => {
10083
+ let key = "key";
10084
+ let t = initializeTmTest();
10085
+
10086
+ // Start a fetch load and interrupt with a navigation
10087
+ let A = await t.fetch("/foo", key, "root");
10088
+ let B = await t.navigate("/bar", undefined, ["foo"]);
10089
+
10090
+ // The fetcher loader redirect should be ignored
10091
+ await A.loaders.foo.redirect("/baz");
10092
+ expect(t.router.state).toMatchObject({
10093
+ navigation: { location: { pathname: "/bar" } },
10094
+ location: { pathname: "/" },
10095
+ });
10096
+ expect(t.router.state.fetchers.get(key)?.state).toBe("loading");
10097
+
10098
+ // The navigation should trigger the fetcher to revalidate since it's
10099
+ // not yet "completed". If it redirects again we should follow that
10100
+ await B.loaders.bar.resolve("BAR");
10101
+ let C = await B.loaders.foo.redirect(
10102
+ "/foo/bar",
10103
+ undefined,
10104
+ undefined,
10105
+ ["foo"]
10106
+ );
10107
+ expect(t.router.state).toMatchObject({
10108
+ navigation: { location: { pathname: "/foo/bar" } },
10109
+ location: { pathname: "/" },
10110
+ loaderData: {
10111
+ root: "ROOT",
10112
+ },
10113
+ });
10114
+ expect(t.router.state.fetchers.get(key)?.state).toBe("loading");
10115
+
10116
+ // The fetcher should not revalidate here since it triggered the redirect
10117
+ await C.loaders.foobar.resolve("FOOBAR");
10118
+ expect(t.router.state).toMatchObject({
10119
+ navigation: IDLE_NAVIGATION,
10120
+ location: { pathname: "/foo/bar" },
10121
+ loaderData: {
10122
+ root: "ROOT",
10123
+ foobar: "FOOBAR",
10124
+ },
10125
+ });
10126
+ expect(t.router.state.fetchers.get(key)?.state).toBe("idle");
10127
+ expect(t.router.state.fetchers.get(key)?.data).toBe(undefined);
10128
+ });
10129
+
10130
+ it("handle multiple fetcher loader redirects", async () => {
10131
+ let keyA = "a";
10132
+ let keyB = "b";
10133
+ let t = initializeTmTest();
10134
+
10135
+ // Start 2 fetch loads
10136
+ let A = await t.fetch("/foo", keyA, "root");
10137
+ let B = await t.fetch("/bar", keyB, "root");
10138
+
10139
+ // Return a redirect from the second fetcher.load (which will trigger
10140
+ // a revalidation of the first fetcher)
10141
+ let C = await B.loaders.bar.redirect("/baz", undefined, undefined, [
10142
+ "foo",
10143
+ ]);
10144
+ expect(t.router.state).toMatchObject({
10145
+ navigation: { location: { pathname: "/baz" } },
10146
+ location: { pathname: "/" },
10147
+ });
10148
+ expect(t.router.state.fetchers.get(keyA)?.state).toBe("loading");
10149
+ expect(t.router.state.fetchers.get(keyB)?.state).toBe("loading");
10150
+
10151
+ // The original fetch load redirect should be ignored
10152
+ await A.loaders.foo.redirect("/foo/bar");
10153
+ expect(t.router.state).toMatchObject({
10154
+ navigation: { location: { pathname: "/baz" } },
10155
+ location: { pathname: "/" },
10156
+ });
10157
+ expect(t.router.state.fetchers.get(keyA)?.state).toBe("loading");
10158
+ expect(t.router.state.fetchers.get(keyB)?.state).toBe("loading");
10159
+
10160
+ // Resolve the navigation loader and the revalidating (first) fetcher
10161
+ // loader which redirects again
10162
+ await C.loaders.baz.resolve("BAZ");
10163
+ let D = await C.loaders.foo.redirect("/foo/bar");
10164
+ expect(t.router.state).toMatchObject({
10165
+ navigation: { location: { pathname: "/foo/bar" } },
10166
+ location: { pathname: "/" },
10167
+ loaderData: {
10168
+ root: "ROOT",
10169
+ },
10170
+ });
10171
+ expect(t.router.state.fetchers.get(keyA)?.state).toBe("loading");
10172
+ expect(t.router.state.fetchers.get(keyB)?.state).toBe("loading");
10173
+
10174
+ // Resolve the navigation loader, bringing everything back to idle at
10175
+ // the final location
10176
+ await D.loaders.foobar.resolve("FOOBAR");
10177
+ expect(t.router.state).toMatchObject({
10178
+ navigation: IDLE_NAVIGATION,
10179
+ location: { pathname: "/foo/bar" },
10180
+ loaderData: {
10181
+ root: "ROOT",
10182
+ foobar: "FOOBAR",
10183
+ },
10184
+ });
10185
+ expect(t.router.state.fetchers.get(keyA)?.state).toBe("idle");
10186
+ expect(t.router.state.fetchers.get(keyB)?.state).toBe("idle");
10187
+ });
10188
+ });
10012
10189
});
10013
10190
10014
10191
describe("fetcher revalidation", () => {
0 commit comments