1- import { test , expect } from "@playwright/test" ;
1+ import { type Page , test , expect } from "@playwright/test" ;
22
33import { PlaywrightFixture } from "./helpers/playwright-fixture.js" ;
44import {
@@ -10,6 +10,17 @@ import {
1010 js ,
1111} from "./helpers/create-fixture.js" ;
1212
13+ const ANY_CSS_LINK_SELECTOR = "link[rel='stylesheet'][href*='css-component']" ;
14+ // Links with a trailing hash are only ever managed by React Router, not
15+ // Vite's dynamic CSS injection logic
16+ const ROUTE_CSS_LINK_SELECTOR = `${ ANY_CSS_LINK_SELECTOR } [href$='#']` ;
17+
18+ function getCssComponentColor ( page : Page ) {
19+ return page
20+ . locator ( "[data-css-component]" )
21+ . evaluate ( ( el ) => window . getComputedStyle ( el ) . color ) ;
22+ }
23+
1324test . describe ( "Vite CSS lazy loading" , ( ) => {
1425 let fixture : Fixture ;
1526 let appFixture : AppFixture ;
@@ -34,7 +45,7 @@ test.describe("Vite CSS lazy loading", () => {
3445
3546 "app/components/load-lazy-css-component.tsx" : js `
3647 import { lazy, useState } from "react";
37- const LazyCssComponent = lazy(() => import("./css-component"));
48+ export const LazyCssComponent = lazy(() => import("./css-component"));
3849 export function LoadLazyCssComponent() {
3950 const [show, setShow] = useState(false);
4051 return (
@@ -58,14 +69,19 @@ test.describe("Vite CSS lazy loading", () => {
5869 <Link to="/">Home</Link>
5970 </li>
6071 <li>
61- <Link to="/with-css-component">Route with CSS Component</Link>
72+ <Link to="/parent-child/with-css-component">Parent / Route with CSS Component</Link>
73+ </li>
74+ <li>
75+ <Link to="/parent-child/without-css-component">Parent / Route Without CSS Component</Link>
6276 </li>
6377 <li>
64- <Link to="/without-css-component">Route Without CSS Component</Link>
78+ <Link to="/siblings/with-css-component">Siblings / Route with CSS Component</Link>
79+ </li>
80+ <li>
81+ <Link to="/siblings/with-lazy-css-component">Siblings / Route with Lazy CSS Component</Link>
6582 </li>
6683 </ul>
6784 </nav>
68- <LoadLazyCssComponent />
6985 <Outlet />
7086 </>
7187 );
@@ -78,21 +94,71 @@ test.describe("Vite CSS lazy loading", () => {
7894 }
7995 ` ,
8096
81- "app/routes/_layout.with-css-component.tsx" : js `
97+ "app/routes/_layout.parent-child.tsx" : js `
98+ import { Outlet } from "react-router";
99+ import { LoadLazyCssComponent } from "../components/load-lazy-css-component";
100+ export default function Parent() {
101+ return (
102+ <>
103+ <h2 data-route-parent>Parent / Child</h2>
104+ <LoadLazyCssComponent />
105+ <Outlet />
106+ </>
107+ );
108+ }
109+ ` ,
110+
111+ "app/routes/_layout.parent-child.with-css-component.tsx" : js `
82112 import CssComponent from "../components/css-component";
83113 export default function RouteWithCssComponent() {
84114 return (
85115 <>
86- <h2 data-route-with-css-component>Route with CSS Component</h2>
116+ <h2 data-route-child- with-css-component>Route with CSS Component</h2>
87117 <CssComponent />
88118 </>
89119 );
90120 }
91121 ` ,
92122
93- "app/routes/_layout.without-css-component.tsx" : js `
123+ "app/routes/_layout.parent-child. without-css-component.tsx" : js `
94124 export default function RouteWithoutCssComponent() {
95- return <h2 data-route-without-css-component>Route Without CSS Component</h2>;
125+ return <h2 data-route-child-without-css-component>Route Without CSS Component</h2>;
126+ }
127+ ` ,
128+
129+ "app/routes/_layout.siblings.tsx" : js `
130+ import { Outlet } from "react-router";
131+ export default function Siblings() {
132+ return (
133+ <>
134+ <h2 data-route-siblings>Siblings</h2>
135+ <Outlet />
136+ </>
137+ );
138+ }
139+ ` ,
140+
141+ "app/routes/_layout.siblings.with-css-component.tsx" : js `
142+ import CssComponent from "../components/css-component";
143+ export default function SiblingsWithCssComponent() {
144+ return (
145+ <>
146+ <h2 data-route-siblings-with-css-component>Siblings / Route with CSS Component</h2>
147+ <CssComponent />
148+ </>
149+ );
150+ }
151+ ` ,
152+
153+ "app/routes/_layout.siblings.with-lazy-css-component.tsx" : js `
154+ import { LazyCssComponent } from "../components/load-lazy-css-component";
155+ export default function SiblingsWithLazyCssComponent() {
156+ return (
157+ <>
158+ <h2 data-route-siblings-with-lazy-css-component>Siblings / Route with Lazy CSS Component</h2>
159+ <LazyCssComponent />
160+ </>
161+ );
96162 }
97163 ` ,
98164 } ,
@@ -105,42 +171,53 @@ test.describe("Vite CSS lazy loading", () => {
105171 appFixture . close ( ) ;
106172 } ) ;
107173
108- test ( "retains CSS from dynamic imports on navigation if the same CSS is also imported by a route" , async ( {
174+ test ( "retains CSS from dynamic imports in a parent route on navigation if the same CSS is a static dependency of a child route" , async ( {
109175 page,
110176 } ) => {
111177 let app = new PlaywrightFixture ( appFixture , page ) ;
112178
113- const ANY_CSS_LINK_SELECTOR =
114- "link[rel='stylesheet'][href*='css-component']" ;
115- // Links with a trailing hash are only ever managed by React Router, not
116- // Vite's dynamic CSS injection logic
117- const ROUTE_CSS_LINK_SELECTOR = `${ ANY_CSS_LINK_SELECTOR } [href$='#']` ;
118-
119- function getCssComponentColor ( ) {
120- return page
121- . locator ( "[data-css-component]" )
122- . evaluate ( ( el ) => window . getComputedStyle ( el ) . color ) ;
123- }
124-
125- await app . goto ( "/with-css-component" ) ;
126- await page . waitForSelector ( "[data-route-with-css-component]" ) ;
179+ await app . goto ( "/parent-child/with-css-component" ) ;
180+ await page . waitForSelector ( "[data-route-child-with-css-component]" ) ;
127181 expect ( await page . locator ( "[data-css-component]" ) . count ( ) ) . toBe ( 1 ) ;
128182 expect ( await page . locator ( ANY_CSS_LINK_SELECTOR ) . count ( ) ) . toBe ( 1 ) ;
129183 expect ( await page . locator ( ROUTE_CSS_LINK_SELECTOR ) . count ( ) ) . toBe ( 1 ) ;
130184
131- expect ( await getCssComponentColor ( ) ) . toBe ( "rgb(0, 128, 0)" ) ;
185+ expect ( await getCssComponentColor ( page ) ) . toBe ( "rgb(0, 128, 0)" ) ;
132186
133187 await page . locator ( "[data-load-lazy-css-component]" ) . click ( ) ;
134188 await page . waitForSelector ( "[data-css-component]" ) ;
135189 expect ( await page . locator ( ANY_CSS_LINK_SELECTOR ) . count ( ) ) . toBe ( 2 ) ;
136190 expect ( await page . locator ( ROUTE_CSS_LINK_SELECTOR ) . count ( ) ) . toBe ( 1 ) ;
137191
138- await app . clickLink ( "/without-css-component" ) ;
139- await page . waitForSelector ( "[data-route-without-css-component]" ) ;
192+ await app . clickLink ( "/parent-child/ without-css-component" ) ;
193+ await page . waitForSelector ( "[data-route-child- without-css-component]" ) ;
140194 expect ( await page . locator ( "[data-css-component]" ) . count ( ) ) . toBe ( 1 ) ;
141195 expect ( await page . locator ( ANY_CSS_LINK_SELECTOR ) . count ( ) ) . toBe ( 1 ) ;
142196 expect ( await page . locator ( ROUTE_CSS_LINK_SELECTOR ) . count ( ) ) . toBe ( 0 ) ;
143197
144- expect ( await getCssComponentColor ( ) ) . toBe ( "rgb(0, 128, 0)" ) ;
198+ expect ( await getCssComponentColor ( page ) ) . toBe ( "rgb(0, 128, 0)" ) ;
199+ } ) ;
200+
201+ test ( "supports CSS lazy loading when navigating to a sibling route if the current route has a static dependency on the same CSS" , async ( {
202+ page,
203+ } ) => {
204+ let app = new PlaywrightFixture ( appFixture , page ) ;
205+
206+ await app . goto ( "/siblings/with-css-component" ) ;
207+ await page . waitForSelector ( "[data-route-siblings-with-css-component]" ) ;
208+ expect ( await page . locator ( "[data-css-component]" ) . count ( ) ) . toBe ( 1 ) ;
209+ expect ( await page . locator ( ANY_CSS_LINK_SELECTOR ) . count ( ) ) . toBe ( 1 ) ;
210+ expect ( await page . locator ( ROUTE_CSS_LINK_SELECTOR ) . count ( ) ) . toBe ( 1 ) ;
211+ expect ( await getCssComponentColor ( page ) ) . toBe ( "rgb(0, 128, 0)" ) ;
212+
213+ await app . clickLink ( "/siblings/with-lazy-css-component" ) ;
214+ await page . waitForSelector ( "[data-route-siblings-with-lazy-css-component]" ) ;
215+
216+ expect ( await page . locator ( "[data-css-component]" ) . count ( ) ) . toBe ( 1 ) ;
217+
218+ expect ( await page . locator ( ANY_CSS_LINK_SELECTOR ) . count ( ) ) . toBe ( 1 ) ;
219+ expect ( await page . locator ( ROUTE_CSS_LINK_SELECTOR ) . count ( ) ) . toBe ( 0 ) ;
220+
221+ expect ( await getCssComponentColor ( page ) ) . toBe ( "rgb(0, 128, 0)" ) ;
145222 } ) ;
146223} ) ;
0 commit comments