|
1 | | -import { describe, expect, test } from 'vitest' |
2 | | -import { act, render, screen } from '@testing-library/react' |
| 1 | +import { afterEach, describe, expect, test } from 'vitest' |
| 2 | +import { |
| 3 | + act, |
| 4 | + cleanup, |
| 5 | + fireEvent, |
| 6 | + render, |
| 7 | + screen, |
| 8 | + waitFor, |
| 9 | +} from '@testing-library/react' |
| 10 | +import { createPortal } from 'react-dom' |
3 | 11 | import ReactDOMServer from 'react-dom/server' |
4 | 12 |
|
5 | 13 | import { |
6 | 14 | HeadContent, |
| 15 | + Link, |
7 | 16 | Outlet, |
8 | 17 | RouterProvider, |
| 18 | + createBrowserHistory, |
9 | 19 | createMemoryHistory, |
10 | 20 | createRootRoute, |
11 | 21 | createRoute, |
12 | 22 | createRouter, |
13 | 23 | } from '../src' |
14 | 24 | import { Scripts } from '../src/Scripts' |
15 | 25 |
|
| 26 | +afterEach(() => { |
| 27 | + window.history.replaceState(null, 'root', '/') |
| 28 | + cleanup() |
| 29 | +}) |
| 30 | + |
16 | 31 | describe('ssr scripts', () => { |
17 | 32 | test('it works', async () => { |
18 | 33 | const rootRoute = createRootRoute({ |
@@ -327,6 +342,179 @@ describe('ssr HeadContent', () => { |
327 | 342 | `<title>Index</title><meta name="image" content="image.jpg"/><meta property="og:description" content="Root description"/><meta name="description" content="Index"/><meta name="last-modified" content="2021-10-10"/><meta property="og:image" content="index-image.jpg"/>`, |
328 | 343 | ) |
329 | 344 | }) |
| 345 | + |
| 346 | + test('keeps manifest stylesheet links mounted when history state changes', async () => { |
| 347 | + const history = createBrowserHistory() |
| 348 | + |
| 349 | + try { |
| 350 | + const rootRoute = createRootRoute({ |
| 351 | + component: () => { |
| 352 | + return ( |
| 353 | + <> |
| 354 | + {createPortal(<HeadContent />, document.head)} |
| 355 | + <button |
| 356 | + onClick={() => { |
| 357 | + window.history.replaceState( |
| 358 | + { slideId: 'slide-2' }, |
| 359 | + '', |
| 360 | + window.location.href, |
| 361 | + ) |
| 362 | + }} |
| 363 | + > |
| 364 | + Replace state |
| 365 | + </button> |
| 366 | + <Outlet /> |
| 367 | + </> |
| 368 | + ) |
| 369 | + }, |
| 370 | + }) |
| 371 | + |
| 372 | + const indexRoute = createRoute({ |
| 373 | + path: '/', |
| 374 | + getParentRoute: () => rootRoute, |
| 375 | + component: () => <div>Index</div>, |
| 376 | + }) |
| 377 | + |
| 378 | + const router = createRouter({ |
| 379 | + history, |
| 380 | + routeTree: rootRoute.addChildren([indexRoute]), |
| 381 | + }) |
| 382 | + |
| 383 | + router.ssr = { |
| 384 | + manifest: { |
| 385 | + routes: { |
| 386 | + [rootRoute.id]: { |
| 387 | + assets: [ |
| 388 | + { |
| 389 | + tag: 'link', |
| 390 | + attrs: { |
| 391 | + rel: 'stylesheet', |
| 392 | + href: '/main.css', |
| 393 | + }, |
| 394 | + }, |
| 395 | + ], |
| 396 | + }, |
| 397 | + }, |
| 398 | + }, |
| 399 | + } as any |
| 400 | + |
| 401 | + await router.load() |
| 402 | + |
| 403 | + await act(() => render(<RouterProvider router={router} />)) |
| 404 | + |
| 405 | + const getStylesheetLink = () => |
| 406 | + Array.from(document.querySelectorAll('link[rel="stylesheet"]')).find( |
| 407 | + (link) => link.getAttribute('href') === '/main.css', |
| 408 | + ) |
| 409 | + |
| 410 | + await waitFor(() => { |
| 411 | + expect(getStylesheetLink()).toBeInstanceOf(HTMLLinkElement) |
| 412 | + }) |
| 413 | + |
| 414 | + const initialLink = getStylesheetLink() |
| 415 | + expect(initialLink).toBeInstanceOf(HTMLLinkElement) |
| 416 | + |
| 417 | + fireEvent.click(screen.getByRole('button', { name: 'Replace state' })) |
| 418 | + |
| 419 | + await waitFor(() => { |
| 420 | + expect( |
| 421 | + (router.state.location.state as { slideId?: string }).slideId, |
| 422 | + ).toBe('slide-2') |
| 423 | + }) |
| 424 | + |
| 425 | + expect(getStylesheetLink()).toBe(initialLink) |
| 426 | + expect( |
| 427 | + Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter( |
| 428 | + (link) => link.getAttribute('href') === '/main.css', |
| 429 | + ), |
| 430 | + ).toHaveLength(1) |
| 431 | + } finally { |
| 432 | + history.destroy() |
| 433 | + } |
| 434 | + }) |
| 435 | + |
| 436 | + test('keeps manifest stylesheet links mounted when navigating with Link', async () => { |
| 437 | + const history = createBrowserHistory() |
| 438 | + |
| 439 | + try { |
| 440 | + const rootRoute = createRootRoute({ |
| 441 | + component: () => { |
| 442 | + return ( |
| 443 | + <> |
| 444 | + {createPortal(<HeadContent />, document.head)} |
| 445 | + <Outlet /> |
| 446 | + </> |
| 447 | + ) |
| 448 | + }, |
| 449 | + }) |
| 450 | + |
| 451 | + const indexRoute = createRoute({ |
| 452 | + path: '/', |
| 453 | + getParentRoute: () => rootRoute, |
| 454 | + component: () => <Link to="/about">Go to about page</Link>, |
| 455 | + }) |
| 456 | + |
| 457 | + const aboutRoute = createRoute({ |
| 458 | + path: '/about', |
| 459 | + getParentRoute: () => rootRoute, |
| 460 | + component: () => <div>About</div>, |
| 461 | + }) |
| 462 | + |
| 463 | + const router = createRouter({ |
| 464 | + history, |
| 465 | + routeTree: rootRoute.addChildren([indexRoute, aboutRoute]), |
| 466 | + }) |
| 467 | + |
| 468 | + router.ssr = { |
| 469 | + manifest: { |
| 470 | + routes: { |
| 471 | + [rootRoute.id]: { |
| 472 | + assets: [ |
| 473 | + { |
| 474 | + tag: 'link', |
| 475 | + attrs: { |
| 476 | + rel: 'stylesheet', |
| 477 | + href: '/main.css', |
| 478 | + }, |
| 479 | + }, |
| 480 | + ], |
| 481 | + }, |
| 482 | + }, |
| 483 | + }, |
| 484 | + } as any |
| 485 | + |
| 486 | + await router.load() |
| 487 | + |
| 488 | + await act(() => render(<RouterProvider router={router} />)) |
| 489 | + |
| 490 | + const getStylesheetLink = () => |
| 491 | + Array.from(document.querySelectorAll('link[rel="stylesheet"]')).find( |
| 492 | + (link) => link.getAttribute('href') === '/main.css', |
| 493 | + ) |
| 494 | + |
| 495 | + await waitFor(() => { |
| 496 | + expect(getStylesheetLink()).toBeInstanceOf(HTMLLinkElement) |
| 497 | + }) |
| 498 | + |
| 499 | + const initialLink = getStylesheetLink() |
| 500 | + expect(initialLink).toBeInstanceOf(HTMLLinkElement) |
| 501 | + |
| 502 | + fireEvent.click(screen.getByRole('link', { name: 'Go to about page' })) |
| 503 | + |
| 504 | + await waitFor(() => { |
| 505 | + expect(router.state.location.pathname).toBe('/about') |
| 506 | + }) |
| 507 | + |
| 508 | + expect(getStylesheetLink()).toBe(initialLink) |
| 509 | + expect( |
| 510 | + Array.from(document.querySelectorAll('link[rel="stylesheet"]')).filter( |
| 511 | + (link) => link.getAttribute('href') === '/main.css', |
| 512 | + ), |
| 513 | + ).toHaveLength(1) |
| 514 | + } finally { |
| 515 | + history.destroy() |
| 516 | + } |
| 517 | + }) |
330 | 518 | }) |
331 | 519 |
|
332 | 520 | describe('data script rendering', () => { |
|
0 commit comments