@@ -5,6 +5,7 @@ import type { Fixture, AppFixture } from "./helpers/create-fixture.js";
55import {
66 createAppFixture ,
77 createFixture ,
8+ css ,
89 js ,
910} from "./helpers/create-fixture.js" ;
1011
@@ -64,27 +65,113 @@ test.beforeAll(async () => {
6465 // `createFixture` will make an app and run your tests against it.
6566 ////////////////////////////////////////////////////////////////////////////
6667 files : {
67- "app/routes/_index.tsx" : js `
68- import { useLoaderData, Link } from "react-router";
68+ "app/routes.ts" : js `
69+ import { type RouteConfig, index, route } from "@react-router/dev/routes";
70+
71+ export default [
72+ index("routes/home.tsx"),
73+ route("company", "routes/layout.tsx", [
74+ route("books", "routes/books/route.tsx"),
75+ route("publishers", "routes/publishers/route.tsx"),
76+ ]),
77+ ] satisfies RouteConfig;
78+ ` ,
79+
80+ "app/components/Icon.module.css" : css `
81+ .icon {
82+ width: 20px;
83+ height: 20px;
84+ background-color: green;
85+ }
86+ ` ,
87+
88+ "app/components/Icon.tsx" : js `
89+ import styles from "./Icon.module.css";
6990
70- export function loader() {
71- return "pizza" ;
91+ export const Icon = () => {
92+ return <div data-testid="icon" className={styles.icon} /> ;
7293 }
94+ ` ,
95+
96+ "app/components/LazyIcon.tsx" : js `
97+ import { lazy, Suspense } from "react";
98+
99+ const Icon = lazy(() =>
100+ import("../components/Icon").then((m) => ({ default: m.Icon }))
101+ );
102+
103+ const LazyIcon = ({ show }: { show: boolean }) => {
104+ if (!show) return null;
73105
74- export default function Index() {
75- let data = useLoaderData();
76106 return (
77- <div>
78- {data}
79- <Link to="/burgers">Other Route</Link>
107+ <Suspense fallback={<div>Loading...</div>}>
108+ <Icon />
109+ </Suspense>
110+ );
111+ };
112+
113+ export { LazyIcon };
114+ ` ,
115+
116+ "app/routes/home.tsx" : js `
117+ import { redirect } from "react-router";
118+
119+ export const loader = () => {
120+ return redirect("/company/books");
121+ };
122+ ` ,
123+
124+ "app/routes/layout.tsx" : js `
125+ import { Link, Outlet } from "react-router";
126+
127+ import { LazyIcon } from "../components/LazyIcon";
128+ import { useState, useEffect } from "react";
129+
130+ export default function Layout() {
131+ const [hydrated, setHydrated] = useState(false);
132+ const [show, setShow] = useState(false);
133+
134+ useEffect(() => {
135+ setShow(true);
136+ },[])
137+
138+ return (
139+ <div style={{ border: "1px solid blue" }}>
140+ <h1>Layout</h1>
141+ <nav>
142+ <Link to="/company/books">Books</Link>
143+ <Link to="/company/publishers">Publishers</Link>
144+ </nav>
145+ <div>
146+ <LazyIcon show={show} />
147+ </div>
148+ <div style={{ border: "1px solid red" }}>
149+ <Outlet />
150+ </div>
80151 </div>
81- )
152+ );
153+ }
154+ ` ,
155+
156+ "app/routes/books/route.tsx" : js `
157+ import { Icon } from "../../components/Icon";
158+
159+ export default function BooksRoute() {
160+ return (
161+ <>
162+ <h1>Books</h1>
163+ <div>
164+ <Icon />
165+ </div>
166+ </>
167+ );
82168 }
169+
83170 ` ,
84171
85- "app/routes/burgers .tsx" : js `
86- export default function Index () {
87- return <div>cheeseburger</div >;
172+ "app/routes/publishers/route .tsx" : js `
173+ export default function PublishersRoute () {
174+ return <h1>Publishers</h1 >;
88175 }
89176 ` ,
90177 } ,
@@ -103,22 +190,48 @@ test.afterAll(() => {
103190// add a good description for what you expect React Router to do 👇🏽
104191////////////////////////////////////////////////////////////////////////////////
105192
106- test ( "[description of what you expect it to do]" , async ( { page } ) => {
193+ test ( "should preserve the CSS from the lazy loaded component even when it's in the route css manifest" , async ( {
194+ page,
195+ } ) => {
107196 let app = new PlaywrightFixture ( appFixture , page ) ;
108- // You can test any request your app might get using `fixture`.
109- let response = await fixture . requestDocument ( "/" ) ;
110- expect ( await response . text ( ) ) . toMatch ( "pizza" ) ;
111197
112198 // If you need to test interactivity use the `app`
113199 await app . goto ( "/" ) ;
114- await app . clickLink ( "/burgers" ) ;
115- await page . waitForSelector ( "text=cheeseburger" ) ;
116200
117- // If you're not sure what's going on, you can "poke" the app, it'll
118- // automatically open up in your browser for 20 seconds, so be quick!
119- // await app.poke(20);
201+ expect ( ( await page . $$ ( "data-testid=icon" ) ) . length ) . toBe ( 1 ) ;
202+
203+ // check the head for a link to the css that includes the word `Icon`
204+ const links1 = await page . $$ ( "link" ) ;
205+ let found1 = false ;
206+ for ( const link of links1 ) {
207+ const href = await link . getAttribute ( "href" ) ;
208+ if ( href ?. includes ( "Icon" ) && href . includes ( "css" ) ) {
209+ found1 = true ;
210+ }
211+ }
212+
213+ expect ( found1 ) . toBe ( true ) ;
214+
215+ // wait for the loading to be gone
216+ await expect ( page . getByText ( "Loading..." ) ) . toHaveCount ( 0 ) ;
217+
218+ // check there are two data-testid=icon elements
219+ expect ( await page . getByTestId ( "icon" ) . all ( ) ) . toHaveLength ( 2 ) ;
220+
221+ await app . clickLink ( "/company/publishers" ) ;
222+
223+ expect ( await page . getByTestId ( "icon" ) . all ( ) ) . toHaveLength ( 1 ) ;
224+
225+ const links2 = await page . $$ ( "link" ) ;
226+ let found2 = false ;
227+ for ( const link of links2 ) {
228+ const href = await link . getAttribute ( "href" ) ;
229+ if ( href ?. includes ( "Icon" ) && href . includes ( "css" ) ) {
230+ found2 = true ;
231+ }
232+ }
120233
121- // Go check out the other tests to see what else you can do.
234+ expect ( found2 ) . toBe ( true ) ;
122235} ) ;
123236
124237////////////////////////////////////////////////////////////////////////////////
0 commit comments