diff --git a/integration/flat-routes-test.ts b/integration/flat-routes-test.ts index 021122adfe8..9254ca16ee5 100644 --- a/integration/flat-routes-test.ts +++ b/integration/flat-routes-test.ts @@ -1,144 +1,204 @@ +import { PassThrough } from "node:stream"; import { test, expect } from "@playwright/test"; import { PlaywrightFixture } from "./helpers/playwright-fixture"; import type { Fixture, AppFixture } from "./helpers/create-fixture"; +import { createFixtureProject } from "./helpers/create-fixture"; import { createAppFixture, createFixture, js } from "./helpers/create-fixture"; let fixture: Fixture; let appFixture: AppFixture; -test.beforeAll(async () => { - fixture = await createFixture({ - future: { v2_routeConvention: true }, - files: { - "app/root.jsx": js` - import { Links, Meta, Outlet, Scripts } from "@remix-run/react"; - - export default function Root() { - return ( - - - - - - -
-

Root

- -
- - - - ); - } - `, - - "app/routes/_index.jsx": js` - export default function () { - return

Index

; - } - `, - - "app/routes/folder/route.jsx": js` - export default function () { - return

Folder (Route.jsx)

; - } - `, - - "app/routes/folder2/index.jsx": js` - export default function () { - return

Folder (Index.jsx)

; - } - `, - - "app/routes/flat.file.jsx": js` - export default function () { - return

Flat File

; - } - `, - - "app/routes/dashboard/route.jsx": js` - import { Outlet } from "@remix-run/react"; - - export default function () { - return ( - <> -

Dashboard Layout

- - - ) - } - `, - - "app/routes/dashboard._index/route.jsx": js` - export default function () { - return

Dashboard Index

; - } - `, - }, +test.describe("flat routes", () => { + test.beforeAll(async () => { + fixture = await createFixture({ + future: { v2_routeConvention: true }, + files: { + "app/root.jsx": js` + import { Links, Meta, Outlet, Scripts } from "@remix-run/react"; + + export default function Root() { + return ( + + + + + + +
+

Root

+ +
+ + + + ); + } + `, + + "app/routes/_index.jsx": js` + export default function () { + return

Index

; + } + `, + + "app/routes/folder/route.jsx": js` + export default function () { + return

Folder (Route.jsx)

; + } + `, + + "app/routes/folder2/index.jsx": js` + export default function () { + return

Folder (Index.jsx)

; + } + `, + + "app/routes/flat.file.jsx": js` + export default function () { + return

Flat File

; + } + `, + + "app/routes/dashboard/route.jsx": js` + import { Outlet } from "@remix-run/react"; + + export default function () { + return ( + <> +

Dashboard Layout

+ + + ) + } + `, + + "app/routes/dashboard._index/route.jsx": js` + export default function () { + return

Dashboard Index

; + } + `, + }, + }); + + appFixture = await createAppFixture(fixture); }); - appFixture = await createAppFixture(fixture); -}); - -test.afterAll(() => { - appFixture.close(); -}); + test.afterAll(() => { + appFixture.close(); + }); -test.describe("without JavaScript", () => { - test.use({ javaScriptEnabled: false }); - runTests(); -}); + test.describe("without JavaScript", () => { + test.use({ javaScriptEnabled: false }); + runTests(); + }); -test.describe("with JavaScript", () => { - test.use({ javaScriptEnabled: true }); - runTests(); -}); + test.describe("with JavaScript", () => { + test.use({ javaScriptEnabled: true }); + runTests(); + }); -function runTests() { - test("renders matching routes (index)", async ({ page }) => { - let app = new PlaywrightFixture(appFixture, page); - await app.goto("/"); - expect(await app.getHtml("#content")).toBe(`
+ function runTests() { + test("renders matching routes (index)", async ({ page }) => { + let app = new PlaywrightFixture(appFixture, page); + await app.goto("/"); + expect(await app.getHtml("#content")).toBe(`

Root

Index

`); - }); + }); - test("renders matching routes (folder route.jsx)", async ({ page }) => { - let app = new PlaywrightFixture(appFixture, page); - await app.goto("/folder"); - expect(await app.getHtml("#content")).toBe(`
+ test("renders matching routes (folder route.jsx)", async ({ page }) => { + let app = new PlaywrightFixture(appFixture, page); + await app.goto("/folder"); + expect(await app.getHtml("#content")).toBe(`

Root

Folder (Route.jsx)

`); - }); + }); - test("renders matching routes (folder index.jsx)", async ({ page }) => { - let app = new PlaywrightFixture(appFixture, page); - await app.goto("/folder2"); - expect(await app.getHtml("#content")).toBe(`
+ test("renders matching routes (folder index.jsx)", async ({ page }) => { + let app = new PlaywrightFixture(appFixture, page); + await app.goto("/folder2"); + expect(await app.getHtml("#content")).toBe(`

Root

Folder (Index.jsx)

`); - }); + }); - test("renders matching routes (flat file)", async ({ page }) => { - let app = new PlaywrightFixture(appFixture, page); - await app.goto("/flat/file"); - expect(await app.getHtml("#content")).toBe(`
+ test("renders matching routes (flat file)", async ({ page }) => { + let app = new PlaywrightFixture(appFixture, page); + await app.goto("/flat/file"); + expect(await app.getHtml("#content")).toBe(`

Root

Flat File

`); - }); + }); - test("renders matching routes (nested)", async ({ page }) => { - let app = new PlaywrightFixture(appFixture, page); - await app.goto("/dashboard"); - expect(await app.getHtml("#content")).toBe(`
+ test("renders matching routes (nested)", async ({ page }) => { + let app = new PlaywrightFixture(appFixture, page); + await app.goto("/dashboard"); + expect(await app.getHtml("#content")).toBe(`

Root

Dashboard Layout

Dashboard Index

`); + }); + } +}); + +test.describe("emits warnings for route conflicts", async () => { + let buildStdio = new PassThrough(); + let buildOutput: string; + + let originalConsoleLog = console.log; + let originalConsoleWarn = console.warn; + let originalConsoleError = console.error; + + test.beforeAll(async () => { + console.log = () => {}; + console.warn = () => {}; + console.error = () => {}; + await createFixtureProject({ + buildStdio, + future: { v2_routeConvention: true }, + files: { + "routes/_dashboard._index.tsx": js` + export default function () { + return

routes/_dashboard._index

; + } + `, + "app/routes/_index.jsx": js` + export default function () { + return

routes._index

; + } + `, + "app/routes/_landing._index.jsx": js` + export default function () { + return

routes/_landing._index

; + } + `, + }, + }); + + let chunks: Buffer[] = []; + buildOutput = await new Promise((resolve, reject) => { + buildStdio.on("data", (chunk) => chunks.push(Buffer.from(chunk))); + buildStdio.on("error", (err) => reject(err)); + buildStdio.on("end", () => + resolve(Buffer.concat(chunks).toString("utf8")) + ); + }); }); -} + + test.afterAll(() => { + console.log = originalConsoleLog; + console.warn = originalConsoleWarn; + console.error = originalConsoleError; + }); + + test("warns about conflicting routes", () => { + console.log(buildOutput); + expect(buildOutput).toContain(`⚠️ Route Path Collision: "/"`); + }); +}); diff --git a/packages/remix-dev/__tests__/flat-routes-test.ts b/packages/remix-dev/__tests__/flat-routes-test.ts index 4cedf056a46..8bb588e7b84 100644 --- a/packages/remix-dev/__tests__/flat-routes-test.ts +++ b/packages/remix-dev/__tests__/flat-routes-test.ts @@ -629,7 +629,7 @@ describe("flatRoutes", () => { let testFiles = [ "routes/_landing._index.tsx", "routes/_dashboard._index.tsx", - "routes/._index.tsx", + "routes/_index.tsx", ]; let routeManifest = flatRoutesUniversal( @@ -650,7 +650,7 @@ describe("flatRoutes", () => { 🟢 routes${path.sep}_landing._index.tsx ⭕️️ routes${path.sep}_dashboard._index.tsx - ⭕️️ routes${path.sep}._index.tsx + ⭕️️ routes${path.sep}_index.tsx `) ); });