|
| 1 | +import { test, expect } from "@playwright/test"; |
| 2 | +import getPort from "get-port"; |
| 3 | + |
| 4 | +import { |
| 5 | + createProject, |
| 6 | + createEditor, |
| 7 | + viteDev, |
| 8 | + VITE_CONFIG, |
| 9 | +} from "./helpers/vite.js"; |
| 10 | + |
| 11 | +const files = { |
| 12 | + "app/routes/_index.tsx": String.raw` |
| 13 | + import { useState, useEffect } from "react"; |
| 14 | + import { Link } from "@remix-run/react"; |
| 15 | +
|
| 16 | + export default function IndexRoute() { |
| 17 | + const [mounted, setMounted] = useState(false); |
| 18 | + useEffect(() => { |
| 19 | + setMounted(true); |
| 20 | + }, []); |
| 21 | +
|
| 22 | + return ( |
| 23 | + <div> |
| 24 | + <p data-mounted>Mounted: {mounted ? "yes" : "no"}</p> |
| 25 | + <Link to="/other">/other</Link> |
| 26 | + </div> |
| 27 | + ); |
| 28 | + } |
| 29 | + `, |
| 30 | + "app/routes/other.tsx": String.raw` |
| 31 | + import { useLoaderData } from "@remix-run/react"; |
| 32 | +
|
| 33 | + export const loader = () => "hello"; |
| 34 | +
|
| 35 | + export default function Route() { |
| 36 | + const loaderData = useLoaderData(); |
| 37 | + return ( |
| 38 | + <div data-loader-data>loaderData = {JSON.stringify(loaderData)}</div> |
| 39 | + ); |
| 40 | + } |
| 41 | + `, |
| 42 | +}; |
| 43 | + |
| 44 | +test.describe(async () => { |
| 45 | + let port: number; |
| 46 | + let cwd: string; |
| 47 | + let stop: () => Promise<void>; |
| 48 | + |
| 49 | + test.beforeAll(async () => { |
| 50 | + port = await getPort(); |
| 51 | + cwd = await createProject({ |
| 52 | + "vite.config.js": await VITE_CONFIG({ port }), |
| 53 | + ...files, |
| 54 | + }); |
| 55 | + stop = await viteDev({ cwd, port }); |
| 56 | + }); |
| 57 | + test.afterAll(async () => await stop()); |
| 58 | + |
| 59 | + test("Vite / dev / invalidate manifest on route exports change", async ({ |
| 60 | + page, |
| 61 | + context, |
| 62 | + browserName, |
| 63 | + }) => { |
| 64 | + let pageErrors: Error[] = []; |
| 65 | + page.on("pageerror", (error) => pageErrors.push(error)); |
| 66 | + let edit = createEditor(cwd); |
| 67 | + |
| 68 | + await page.goto(`http://localhost:${port}`, { waitUntil: "networkidle" }); |
| 69 | + await expect(page.locator("[data-mounted]")).toHaveText("Mounted: yes"); |
| 70 | + expect(pageErrors).toEqual([]); |
| 71 | + |
| 72 | + let originalContents: string; |
| 73 | + |
| 74 | + // Removing loader export in other page should invalidate manifest |
| 75 | + await edit("app/routes/other.tsx", (contents) => { |
| 76 | + originalContents = contents; |
| 77 | + return contents.replace(/export const loader.*/, ""); |
| 78 | + }); |
| 79 | + |
| 80 | + // After browser reload, client should be aware that there's no loader on the other route |
| 81 | + if (browserName === "webkit") { |
| 82 | + // Force new page instance for webkit. |
| 83 | + // Otherwise browser doesn't seem to fetch new manifest probably due to caching. |
| 84 | + page = await context.newPage(); |
| 85 | + } |
| 86 | + await page.goto(`http://localhost:${port}`, { waitUntil: "networkidle" }); |
| 87 | + await expect(page.locator("[data-mounted]")).toHaveText("Mounted: yes"); |
| 88 | + await page.getByRole("link", { name: "/other" }).click(); |
| 89 | + await expect(page.locator("[data-loader-data]")).toHaveText( |
| 90 | + "loaderData = null" |
| 91 | + ); |
| 92 | + expect(pageErrors).toEqual([]); |
| 93 | + |
| 94 | + // Revert route to original state to check HMR works and to ensure the |
| 95 | + // original file contents were valid |
| 96 | + await edit("app/routes/other.tsx", () => originalContents); |
| 97 | + await expect(page.locator("[data-loader-data]")).toHaveText( |
| 98 | + 'loaderData = "hello"' |
| 99 | + ); |
| 100 | + expect(pageErrors).toEqual([]); |
| 101 | + }); |
| 102 | +}); |
0 commit comments