diff --git a/.github/workflows/integration-full.yml b/.github/workflows/integration-full.yml index fd2f79bfa9..205cc2b7f9 100644 --- a/.github/workflows/integration-full.yml +++ b/.github/workflows/integration-full.yml @@ -31,7 +31,7 @@ jobs: uses: ./.github/workflows/shared-integration.yml with: os: "ubuntu-latest" - node_version: "[20, 22]" + node_version: "[22, 24]" browser: '["chromium", "firefox"]' integration-windows: @@ -40,7 +40,7 @@ jobs: uses: ./.github/workflows/shared-integration.yml with: os: "windows-latest" - node_version: "[20, 22]" + node_version: "[22, 24]" browser: '["msedge"]' integration-macos: @@ -49,5 +49,5 @@ jobs: uses: ./.github/workflows/shared-integration.yml with: os: "macos-latest" - node_version: "[20, 22]" + node_version: "[22, 24]" browser: '["webkit"]' diff --git a/.github/workflows/integration-pr-ubuntu.yml b/.github/workflows/integration-pr-ubuntu.yml index ac25713e08..056f0d1e46 100644 --- a/.github/workflows/integration-pr-ubuntu.yml +++ b/.github/workflows/integration-pr-ubuntu.yml @@ -31,5 +31,5 @@ jobs: uses: ./.github/workflows/shared-integration.yml with: os: "ubuntu-latest" - node_version: "[22]" + node_version: "[24]" browser: '["chromium"]' diff --git a/.github/workflows/integration-pr-windows-macos.yml b/.github/workflows/integration-pr-windows-macos.yml index a36aef144d..4423d376b4 100644 --- a/.github/workflows/integration-pr-windows-macos.yml +++ b/.github/workflows/integration-pr-windows-macos.yml @@ -21,7 +21,7 @@ jobs: uses: ./.github/workflows/shared-integration.yml with: os: "ubuntu-latest" - node_version: "[22]" + node_version: "[24]" browser: '["firefox"]' integration-msedge: @@ -29,10 +29,10 @@ jobs: if: github.repository == 'remix-run/react-router' uses: ./.github/workflows/shared-integration.yml with: - os: "windows-latest" - node_version: "[22]" + os: "windows-2025" + node_version: "[24]" browser: '["msedge"]' - timeout: 60 + timeout: 120 integration-webkit: name: "👀 Integration Test" @@ -40,5 +40,5 @@ jobs: uses: ./.github/workflows/shared-integration.yml with: os: "macos-latest" - node_version: "[22]" + node_version: "[24]" browser: '["webkit"]' diff --git a/.github/workflows/shared-integration.yml b/.github/workflows/shared-integration.yml index e7adb0dd47..4dd3df2cfd 100644 --- a/.github/workflows/shared-integration.yml +++ b/.github/workflows/shared-integration.yml @@ -9,7 +9,7 @@ on: node_version: required: true # this is limited to string | boolean | number (https://github.community/t/can-action-inputs-be-arrays/16457) - # but we want to pass an array (node_version: "[20, 22]"), + # but we want to pass an array (node_version: "[22, 24]"), # so we'll need to manually stringify it for now type: string browser: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index af709fdaaa..5eda492b5f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,8 +26,8 @@ jobs: fail-fast: false matrix: node: - - 20 - 22 + - 24 runs-on: ubuntu-latest diff --git a/.nvmrc b/.nvmrc index 2edeafb09d..8fdd954df9 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20 \ No newline at end of file +22 \ No newline at end of file diff --git a/integration/fetcher-test.ts b/integration/fetcher-test.ts index f021ea36f3..f0d36a9110 100644 --- a/integration/fetcher-test.ts +++ b/integration/fetcher-test.ts @@ -247,7 +247,7 @@ test.describe("useFetcher", () => { // a
but Edge puts it in some weird code editor markup: // //"LUNCH"- expect(await app.getHtml()).toContain(LUNCH); + await page.getByText(LUNCH); }); test("Form can hit an action", async ({ page }) => { @@ -264,7 +264,7 @@ test.describe("useFetcher", () => { // abut Edge puts it in some weird code editor markup: // //"LUNCH"- expect(await app.getHtml()).toContain(CHEESESTEAK); + await page.getByText(CHEESESTEAK); }); }); @@ -288,9 +288,7 @@ test.describe("useFetcher", () => { await page.fill("#fetcher-input", "input value"); await app.clickElement("#fetcher-submit-json"); await page.waitForSelector(`#fetcher-idle`); - expect(await app.getHtml()).toMatch( - 'ACTION (application/json) input value"' - ); + await page.getByText('ACTION (application/json) input value"'); }); test("submit can hit an action with null json", async ({ page }) => { @@ -299,7 +297,7 @@ test.describe("useFetcher", () => { await app.clickElement("#fetcher-submit-json-null"); await new Promise((r) => setTimeout(r, 1000)); await page.waitForSelector(`#fetcher-idle`); - expect(await app.getHtml()).toMatch('ACTION (application/json) null"'); + await page.getByText('ACTION (application/json) null"'); }); test("submit can hit an action with text", async ({ page }) => { @@ -308,9 +306,7 @@ test.describe("useFetcher", () => { await page.fill("#fetcher-input", "input value"); await app.clickElement("#fetcher-submit-text"); await page.waitForSelector(`#fetcher-idle`); - expect(await app.getHtml()).toMatch( - 'ACTION (text/plain;charset=UTF-8) input value"' - ); + await page.getByText('ACTION (text/plain;charset=UTF-8) input value"'); }); test("submit can hit an action with empty text", async ({ page }) => { @@ -319,7 +315,7 @@ test.describe("useFetcher", () => { await app.clickElement("#fetcher-submit-text-empty"); await new Promise((r) => setTimeout(r, 1000)); await page.waitForSelector(`#fetcher-idle`); - expect(await app.getHtml()).toMatch('ACTION (text/plain;charset=UTF-8) "'); + await page.getByText('ACTION (text/plain;charset=UTF-8) "'); }); test("submit can hit an action only route", async ({ page }) => { @@ -360,21 +356,19 @@ test.describe("useFetcher", () => { let app = new PlaywrightFixture(appFixture, page); await app.goto("/fetcher-echo", true); - expect(await app.getHtml("pre")).toMatch( - JSON.stringify(["idle/undefined"]) - ); + await page.getByText(JSON.stringify(["idle/undefined"])); await page.fill("#fetcher-input", "1"); await app.clickElement("#fetcher-load"); await page.waitForSelector("#fetcher-idle"); - expect(await app.getHtml("pre")).toMatch( + await page.getByText( JSON.stringify(["idle/undefined", "loading/undefined", "idle/LOADER 1"]) ); await page.fill("#fetcher-input", "2"); await app.clickElement("#fetcher-load"); await page.waitForSelector("#fetcher-idle"); - expect(await app.getHtml("pre")).toMatch( + await page.getByText( JSON.stringify([ "idle/undefined", "loading/undefined", @@ -391,14 +385,12 @@ test.describe("useFetcher", () => { let app = new PlaywrightFixture(appFixture, page); await app.goto("/fetcher-echo", true); - expect(await app.getHtml("pre")).toMatch( - JSON.stringify(["idle/undefined"]) - ); + await page.getByText(JSON.stringify(["idle/undefined"])); await page.fill("#fetcher-input", "1"); await app.clickElement("#fetcher-submit"); await page.waitForSelector("#fetcher-idle"); - expect(await app.getHtml("pre")).toMatch( + await page.getByText( JSON.stringify([ "idle/undefined", "submitting/undefined", @@ -410,7 +402,7 @@ test.describe("useFetcher", () => { await page.fill("#fetcher-input", "2"); await app.clickElement("#fetcher-submit"); await page.waitForSelector("#fetcher-idle"); - expect(await app.getHtml("pre")).toMatch( + await page.getByText( JSON.stringify([ "idle/undefined", "submitting/undefined", diff --git a/integration/helpers/create-fixture.ts b/integration/helpers/create-fixture.ts index 85fbe6fc26..f935390bd8 100644 --- a/integration/helpers/create-fixture.ts +++ b/integration/helpers/create-fixture.ts @@ -337,6 +337,7 @@ export async function createAppFixture(fixture: Fixture, mode?: ServerMode) { return new Promise(async (accept) => { let port = await getPort(); let app = express(); + app.use(express.static(path.join(fixture.projectDir, "public"))); app.use( "/client", express.static(path.join(fixture.projectDir, "build/client")) @@ -460,7 +461,7 @@ function parcelBuild( ) { let parcelBin = "node_modules/parcel/lib/bin.js"; - let buildArgs: string[] = [parcelBin, "build"]; + let buildArgs: string[] = [parcelBin, "build", "--no-cache"]; let buildSpawn = spawnSync("node", buildArgs, { cwd: projectDir, diff --git a/integration/helpers/rsc-parcel-framework/public/favicon.ico b/integration/helpers/rsc-parcel-framework/public/favicon.ico new file mode 100644 index 0000000000..5dbdfcddcb Binary files /dev/null and b/integration/helpers/rsc-parcel-framework/public/favicon.ico differ diff --git a/integration/helpers/vite.ts b/integration/helpers/vite.ts index 707b323d1b..f30f6b699c 100644 --- a/integration/helpers/vite.ts +++ b/integration/helpers/vite.ts @@ -1,4 +1,5 @@ -import { spawn, spawnSync, type ChildProcess } from "node:child_process"; +import type { ChildProcess } from "node:child_process"; +import { sync as spawnSync, spawn } from "cross-spawn"; import { cp, mkdir, readFile, writeFile } from "node:fs/promises"; import { createRequire } from "node:module"; import { platform } from "node:os"; diff --git a/integration/playwright.config.ts b/integration/playwright.config.ts index da6159be6b..965aacd57d 100644 --- a/integration/playwright.config.ts +++ b/integration/playwright.config.ts @@ -18,7 +18,8 @@ const config: PlaywrightTestConfig = { }, /* Maximum time one test can run for. */ timeout: isWindows ? 60_000 : 30_000, - fullyParallel: true, + fullyParallel: !(isWindows && process.env.CI), + workers: isWindows && process.env.CI ? 1 : undefined, expect: { /* Maximum time expect() should wait for the condition to be met. */ timeout: isWindows ? 10_000 : 5_000, diff --git a/integration/revalidate-test.ts b/integration/revalidate-test.ts index b14c73ff5d..5ccdfa79b8 100644 --- a/integration/revalidate-test.ts +++ b/integration/revalidate-test.ts @@ -77,7 +77,7 @@ test.describe("Revalidation", () => { let data = useLoaderData(); return ( <> -{'Value:' + data.value}
+{'Value:' + data.value}
> ); @@ -122,7 +122,7 @@ test.describe("Revalidation", () => { let revalidator = useRevalidator(); return ( <> - {'Value:' + data.value}
+{'Value:' + data.value}