UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

115 lines (114 loc) 3.99 kB
import React from "react"; import TestRenderer, { act } from "react-test-renderer"; import { describe, expect, it } from "vitest"; import { usePathname } from "../hooks.mjs"; import { Root } from "../Root.mjs"; import { Slot } from "../views/Navigator.mjs"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; globalThis.IS_REACT_ACT_ENVIRONMENT = true; globalThis.requestAnimationFrame ??= cb => setTimeout(() => cb(0), 0); globalThis.cancelAnimationFrame ??= id => clearTimeout(id); const routerRoot = "app"; function makeRoutes(modules) { globalThis["__vxrnVersion"] = (globalThis["__vxrnVersion"] ?? 0) + 1; const routes = {}; for (const [file, module] of Object.entries(modules)) { routes[`/${routerRoot}/${file}`] = module; } return routes; } const slotLayout = { default: () => /* @__PURE__ */jsx(Slot, {}) }; const leaf = label => ({ default: () => label }); function routeTreeWithGate(gate) { const Gate = gate; return makeRoutes({ "_layout.tsx": slotLayout, "(app)/_layout.tsx": { default: () => /* @__PURE__ */jsx(Gate, { children: /* @__PURE__ */jsx(Slot, {}) }) }, "(app)/index.tsx": leaf("app-index"), "(app)/project/_layout.tsx": slotLayout, "(app)/project/[projectId]/_layout.tsx": slotLayout, "(app)/project/[projectId]/[sessionId]/_layout.tsx": slotLayout, "(app)/project/[projectId]/[sessionId]/index.tsx": leaf("session"), "(site)/_layout.tsx": slotLayout, "(site)/pricing.tsx": leaf("pricing") }); } function renderAt(path, routes) { let renderer; act(() => { renderer = TestRenderer.create(/* @__PURE__ */jsx(Root, { path, routes, routerRoot, isClient: false })); }); return renderer; } function bodyText(renderer) { const collect = node => typeof node === "string" ? node : Array.isArray(node) ? node.map(collect).join("") : node ? collect(node.children ?? []) : ""; return collect(renderer.toJSON()); } describe("a layout that renders something other than its <Slot />", () => { it("does not move the pathname while the URL stands still", () => { const pathnames = []; let setBlocked; function Probe() { const pathname = usePathname(); if (pathnames[pathnames.length - 1] !== pathname) pathnames.push(pathname); return null; } function Gate({ children }) { const [blocked, set] = React.useState(true); setBlocked = set; return /* @__PURE__ */jsxs(Fragment, { children: [/* @__PURE__ */jsx(Probe, {}), blocked ? "placeholder" : children] }); } const renderer = renderAt("/project/p1/main", routeTreeWithGate(Gate)); expect(bodyText(renderer)).toBe("placeholder"); act(() => setBlocked(false)); expect(bodyText(renderer)).toBe("session"); act(() => setBlocked(true)); expect(bodyText(renderer)).toBe("placeholder"); act(() => setBlocked(false)); expect(pathnames).toEqual(["/project/p1/main"]); }); it("settles when the gate itself reads the pathname", async () => { let renders = 0; let requestBlock; function Gate({ children }) { const pathname = usePathname(); const [blockRequested, setBlockRequested] = React.useState(false); requestBlock = () => setBlockRequested(true); renders++; if (renders > 100) { throw new Error(`gate rendered ${renders} times without settling`); } const blocked = blockRequested && pathname.startsWith("/project/"); return /* @__PURE__ */jsx(Fragment, { children: blocked ? "blocked" : children }); } const renderer = renderAt("/project/p1/main", routeTreeWithGate(Gate)); await act(async () => {}); expect(bodyText(renderer)).toBe("session"); await act(async () => requestBlock()); await act(async () => {}); expect(bodyText(renderer)).toBe("blocked"); expect(renders).toBeLessThan(20); }); }); //# sourceMappingURL=conditionalSlotPathname.test.mjs.map