Skip to content

Commit df8d61c

Browse files
committed
Merge branch 'dev' into markdalgleish/handle-new-config-file
2 parents 6ac624b + 5bb1154 commit df8d61c

File tree

9 files changed

+378
-159
lines changed

9 files changed

+378
-159
lines changed

.changeset/dull-hotels-battle.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": patch
3+
---
4+
5+
When `future.unstable_viteEnvironmentApi` is enabled, allow plugins that override the default SSR environment (such as `@cloudflare/vite-plugin`) to be placed before or after the React Router plugin.

.changeset/eleven-oranges-cheat.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-router/dev": patch
3+
---
4+
5+
Fix conflicts with other Vite plugins that use the `configureServer` and/or `configurePreviewServer` hooks

integration/form-test.ts

+85-77
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,19 @@ test.describe("Forms", () => {
197197

198198
"app/routes/blog._index.tsx": js`
199199
import { Form } from "react-router";
200+
201+
export function loader() {
202+
return { timestamp: Date.now() }
203+
}
204+
200205
export function action() {
201206
return { ok: true };
202207
}
203-
export default function() {
208+
209+
export default function Component({ loaderData }) {
204210
return (
205211
<>
212+
<div id="timestamp">{loaderData.timestamp}</div>
206213
<Form id="${INDEX_ROUTE_NO_ACTION}">
207214
<input type="hidden" name="foo" defaultValue="1" />
208215
<button>Submit</button>
@@ -525,15 +532,15 @@ test.describe("Forms", () => {
525532
}) => {
526533
let app = new PlaywrightFixture(appFixture, page);
527534
await app.goto("/get-submission");
528-
await app.clickElement(`#${FORM_WITH_ACTION_INPUT} button`);
529-
await page.waitForSelector(`pre:has-text("${EAT}")`);
535+
await page.locator(`#${FORM_WITH_ACTION_INPUT} button`).click();
536+
await page.locator(`pre:has-text("${EAT}")`).waitFor();
530537
});
531538

532539
test("posts to a loader with button data with click", async ({ page }) => {
533540
let app = new PlaywrightFixture(appFixture, page);
534541
await app.goto("/get-submission");
535-
await app.clickElement("#buttonWithValue");
536-
await page.waitForSelector(`pre:has-text("${LAKSA}")`);
542+
await page.locator("#buttonWithValue").click();
543+
await page.locator(`pre:has-text("${LAKSA}")`).waitFor();
537544
});
538545

539546
test("posts to a loader with button data with keyboard", async ({
@@ -553,16 +560,16 @@ test.describe("Forms", () => {
553560
test("posts with the correct checkbox data", async ({ page }) => {
554561
let app = new PlaywrightFixture(appFixture, page);
555562
await app.goto("/get-submission");
556-
await app.clickElement(`#${CHECKBOX_BUTTON}`);
557-
await page.waitForSelector(`pre:has-text("${LAKSA}")`);
558-
await page.waitForSelector(`pre:has-text("${CHEESESTEAK}")`);
563+
await page.locator(`#${CHECKBOX_BUTTON}`).click();
564+
await page.locator(`pre:has-text("${LAKSA}")`).waitFor();
565+
await page.locator(`pre:has-text("${CHEESESTEAK}")`).waitFor();
559566
});
560567

561568
test("posts button data from outside the form", async ({ page }) => {
562569
let app = new PlaywrightFixture(appFixture, page);
563570
await app.goto("/get-submission");
564-
await app.clickElement(`#${ORPHAN_BUTTON}`);
565-
await page.waitForSelector(`pre:has-text("${SQUID_INK_HOTDOG}")`);
571+
await page.locator(`#${ORPHAN_BUTTON}`).click();
572+
await page.locator(`pre:has-text("${SQUID_INK_HOTDOG}")`).waitFor();
566573
});
567574

568575
test(
@@ -793,49 +800,53 @@ test.describe("Forms", () => {
793800
}) => {
794801
let app = new PlaywrightFixture(appFixture, page);
795802

803+
const timestamp = page.locator(`#timestamp`);
804+
const form = page.locator(`#${INDEX_ROUTE_NO_ACTION}`);
805+
const submit = page.locator(`#${INDEX_ROUTE_NO_ACTION} button`);
806+
796807
// Start with a query param
797808
await app.goto("/blog?junk=1");
798-
let html = await app.getHtml();
799-
let el = getElement(html, `#${INDEX_ROUTE_NO_ACTION}`);
800-
expect(el.attr("action")).toBe("/blog?index&junk=1");
801-
expect(app.page.url()).toMatch(/\/blog\?junk=1$/);
809+
const t0 = await timestamp.innerText();
810+
await expect(form).toHaveAttribute("action", "/blog?index&junk=1");
811+
expect(page.url()).toMatch(/\/blog\?junk=1$/);
802812

803813
// On submission, we replace existing parameters (reflected in the
804814
// form action) with the values from the form data. We also do not
805815
// need to preserve the index param in the URL on GET submissions
806-
await app.clickElement(`#${INDEX_ROUTE_NO_ACTION} button`);
807-
html = await app.getHtml();
808-
el = getElement(html, `#${INDEX_ROUTE_NO_ACTION}`);
809-
expect(el.attr("action")).toBe("/blog?index&foo=1");
810-
expect(app.page.url()).toMatch(/\/blog\?foo=1$/);
816+
await submit.click();
817+
const t1 = await timestamp.filter({ hasNotText: t0 }).innerText();
818+
await expect(form).toHaveAttribute("action", "/blog?index&foo=1");
819+
expect(page.url()).toMatch(/\/blog\?foo=1$/);
811820

812821
// Does not append duplicate params on re-submissions
813-
await app.clickElement(`#${INDEX_ROUTE_NO_ACTION} button`);
814-
html = await app.getHtml();
815-
el = getElement(html, `#${INDEX_ROUTE_NO_ACTION}`);
816-
expect(el.attr("action")).toBe("/blog?index&foo=1");
817-
expect(app.page.url()).toMatch(/\/blog\?foo=1$/);
822+
await submit.click();
823+
await timestamp.filter({ hasNotText: t1 }).innerText();
824+
await expect(form).toHaveAttribute("action", "/blog?index&foo=1");
825+
expect(page.url()).toMatch(/\/blog\?foo=1$/);
818826
});
819827

820828
test("handles search params correctly on POST submissions", async ({
821829
page,
822830
}) => {
823831
let app = new PlaywrightFixture(appFixture, page);
824832

833+
const timestamp = page.locator(`#timestamp`);
834+
const form = page.locator(`#${INDEX_ROUTE_NO_ACTION_POST}`);
835+
const submit = page.locator(`#${INDEX_ROUTE_NO_ACTION_POST} button`);
836+
825837
// Start with a query param
826838
await app.goto("/blog?junk=1");
827-
let html = await app.getHtml();
828-
let el = getElement(html, `#${INDEX_ROUTE_NO_ACTION_POST}`);
829-
expect(el.attr("action")).toBe("/blog?index&junk=1");
830-
expect(app.page.url()).toMatch(/\/blog\?junk=1$/);
839+
const t0 = await timestamp.innerText();
840+
await expect(form).toHaveAttribute("action", "/blog?index&junk=1");
841+
expect(page.url()).toMatch(/\/blog\?junk=1$/);
831842

832843
// Form action reflects the current params and change them on submission
833-
await app.clickElement(`#${INDEX_ROUTE_NO_ACTION_POST} button`);
834-
html = await app.getHtml();
835-
el = getElement(html, `#${INDEX_ROUTE_NO_ACTION_POST}`);
836-
expect(el.attr("action")).toBe("/blog?index&junk=1");
844+
await submit.click();
845+
await timestamp.filter({ hasNotText: t0 }).innerText();
846+
await expect(form).toHaveAttribute("action", "/blog?index&junk=1");
847+
837848
await page.waitForURL(/\/blog\?index&junk=1$/);
838-
expect(app.page.url()).toMatch(/\/blog\?index&junk=1$/);
849+
expect(page.url()).toMatch(/\/blog\?index&junk=1$/);
839850
});
840851
});
841852

@@ -993,16 +1004,11 @@ test.describe("Forms", () => {
9931004

9941005
let app = new PlaywrightFixture(appFixture, page);
9951006
await app.goto(`/form-method?method=${method}`, true);
996-
await app.clickElement(`text=Submit`);
1007+
await page.getByText("Submit", { exact: true }).click();
9971008
if (method !== "GET") {
998-
await page.waitForSelector("#action-method");
999-
expect(await app.getHtml("pre#action-method")).toBe(
1000-
`<pre id="action-method">${method}</pre>`
1001-
);
1009+
await expect(page.locator("#action-method")).toHaveText(method);
10021010
}
1003-
expect(await app.getHtml("pre#loader-method")).toBe(
1004-
`<pre id="loader-method">GET</pre>`
1005-
);
1011+
await expect(page.locator("#loader-method")).toHaveText("GET");
10061012
});
10071013
});
10081014
});
@@ -1020,16 +1026,13 @@ test.describe("Forms", () => {
10201026
`/form-method?method=${method}&submitterFormMethod=${overrideMethod}`,
10211027
true
10221028
);
1023-
await app.clickElement(`text=Submit with ${overrideMethod}`);
1029+
await page.locator(`text=Submit with ${overrideMethod}`).click();
10241030
if (overrideMethod !== "GET") {
1025-
await page.waitForSelector("#action-method");
1026-
expect(await app.getHtml("pre#action-method")).toBe(
1027-
`<pre id="action-method">${overrideMethod}</pre>`
1031+
await expect(page.locator("pre#action-method")).toHaveText(
1032+
overrideMethod
10281033
);
10291034
}
1030-
expect(await app.getHtml("pre#loader-method")).toBe(
1031-
`<pre id="loader-method">GET</pre>`
1032-
);
1035+
await expect(page.locator("pre#loader-method")).toHaveText("GET");
10331036
});
10341037
});
10351038
});
@@ -1039,33 +1042,33 @@ test.describe("Forms", () => {
10391042
}) => {
10401043
let app = new PlaywrightFixture(appFixture, page);
10411044

1045+
const formData = page.locator("#formData");
1046+
10421047
await app.goto("/submitter");
1043-
await app.clickElement("text=Add Task");
1044-
expect((await app.getElement("#formData")).val()).toBe(
1048+
await page.locator("text=Add Task").click();
1049+
await expect(formData).toHaveValue(
10451050
"tasks=first&tasks=second&tasks=&tasks=last"
10461051
);
10471052

10481053
await app.goto("/submitter");
1049-
await app.clickElement("text=No Name");
1050-
expect((await app.getElement("#formData")).val()).toBe(
1051-
"tasks=first&tasks=second&tasks=last"
1052-
);
1054+
await page.locator("text=No Name").click();
1055+
await expect(formData).toHaveValue("tasks=first&tasks=second&tasks=last");
10531056

10541057
await app.goto("/submitter");
1055-
await app.clickElement("[alt='Add Task']");
1056-
expect((await app.getElement("#formData")).val()).toMatch(
1058+
await page.locator("[alt='Add Task']").click();
1059+
await expect(formData).toHaveValue(
10571060
/^tasks=first&tasks=second&tasks.x=\d+&tasks.y=\d+&tasks=last$/
10581061
);
10591062

10601063
await app.goto("/submitter");
1061-
await app.clickElement("[alt='No Name']");
1062-
expect((await app.getElement("#formData")).val()).toMatch(
1064+
await page.locator("[alt='No Name']").click();
1065+
await expect(formData).toHaveValue(
10631066
/^tasks=first&tasks=second&x=\d+&y=\d+&tasks=last$/
10641067
);
10651068

10661069
await app.goto("/submitter");
1067-
await app.clickElement("text=Outside");
1068-
expect((await app.getElement("#formData")).val()).toBe(
1070+
await page.locator("text=Outside").click();
1071+
await expect(formData).toHaveValue(
10691072
"tasks=outside&tasks=first&tasks=second&tasks=last"
10701073
);
10711074
});
@@ -1076,23 +1079,23 @@ test.describe("Forms", () => {
10761079
let app = new PlaywrightFixture(appFixture, page);
10771080
let myFile = fixture.projectDir + "/myfile.txt";
10781081

1082+
const formData = page.locator("#formData");
1083+
const submit = page.locator("button");
1084+
10791085
await app.goto("/file-upload");
10801086
await app.uploadFile(`[name=filey]`, myFile);
10811087
await app.uploadFile(`[name=filey2]`, myFile, myFile);
1082-
await app.clickElement("button");
1083-
await page.waitForSelector("#formData");
1084-
1085-
expect((await app.getElement("#formData")).val()).toBe(
1088+
await submit.click();
1089+
await expect(formData).toHaveValue(
10861090
"filey=myfile.txt&filey2=myfile.txt&filey2=myfile.txt&filey3="
10871091
);
10881092

10891093
await app.goto("/file-upload?method=post");
10901094
await app.uploadFile(`[name=filey]`, myFile);
10911095
await app.uploadFile(`[name=filey2]`, myFile, myFile);
1092-
await app.clickElement("button");
1093-
await page.waitForSelector("#formData");
1096+
await submit.click();
10941097

1095-
expect((await app.getElement("#formData")).val()).toBe(
1098+
await expect(formData).toHaveValue(
10961099
"filey=myfile.txt&filey2=myfile.txt&filey2=myfile.txt&filey3="
10971100
);
10981101
});
@@ -1119,20 +1122,25 @@ test.describe("Forms", () => {
11191122
}) => {
11201123
let app = new PlaywrightFixture(appFixture, page);
11211124
await app.goto("/pathless-layout-parent/nested");
1122-
let html = await app.getHtml();
1123-
expect(html).toMatch("Pathless Layout Parent");
1124-
expect(html).toMatch("Pathless Layout ");
1125-
expect(html).toMatch("Pathless Layout Index");
11261125

1127-
let el = getElement(html, `form`);
1128-
expect(el.attr("action")).toBe("/pathless-layout-parent");
1126+
await expect(
1127+
page.getByText("Pathless Layout Parent", { exact: true })
1128+
).toBeVisible();
1129+
await expect(
1130+
page.getByText("Pathless Layout", { exact: true })
1131+
).toBeVisible();
1132+
await expect(
1133+
page.getByText("Pathless Layout Index", { exact: true })
1134+
).toBeVisible();
1135+
1136+
const form = page.locator("form");
1137+
await expect(form).toHaveAttribute("action", "/pathless-layout-parent");
11291138

1130-
expect(await app.getHtml()).toMatch("Submitted - No");
1139+
await expect(page.getByText("Submitted - No")).toBeVisible();
11311140
// This submission should ignore the index route and the pathless layout
11321141
// route above it and hit the action in routes/pathless-layout-parent.jsx
1133-
await app.clickSubmitButton("/pathless-layout-parent");
1134-
await page.waitForSelector("text=Submitted - Yes");
1135-
expect(await app.getHtml()).toMatch("Submitted - Yes");
1142+
await page.getByRole("button").click();
1143+
await expect(page.getByText("Submitted - Yes")).toBeVisible();
11361144
});
11371145
}
11381146
});

integration/helpers/vite-plugin-cloudflare-template/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"serialize-javascript": "^6.0.1"
1919
},
2020
"devDependencies": {
21-
"@cloudflare/vite-plugin": "^0.1.1",
21+
"@cloudflare/vite-plugin": "^0.1.9",
2222
"@cloudflare/workers-types": "^4.20250214.0",
2323
"@react-router/dev": "workspace:*",
2424
"@react-router/fs-routes": "workspace:*",

integration/playwright.config.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { devices } from "@playwright/test";
66
process.env.NODE_OPTIONS =
77
(process.env.NODE_OPTIONS ?? "") + ` --no-warnings=ExperimentalWarning`;
88

9+
const isWindows = process.platform === "win32";
10+
911
const config: PlaywrightTestConfig = {
1012
testDir: ".",
1113
testMatch: ["**/*-test.ts"],
@@ -15,11 +17,11 @@ const config: PlaywrightTestConfig = {
1517
external: ["**/packages/**/*"],
1618
},
1719
/* Maximum time one test can run for. */
18-
timeout: process.platform === "win32" ? 60_000 : 30_000,
20+
timeout: isWindows ? 60_000 : 30_000,
1921
fullyParallel: true,
2022
expect: {
2123
/* Maximum time expect() should wait for the condition to be met. */
22-
timeout: 5_000,
24+
timeout: isWindows ? 10_000 : 5_000,
2325
},
2426
forbidOnly: !!process.env.CI,
2527
retries: process.env.CI ? 3 : 0,

0 commit comments

Comments
 (0)