UNPKG

one

Version:

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

217 lines 11.2 kB
var import_vitest = require("vitest"), import_prefetchIntent = require("./prefetchIntent.cjs"); const rect = (x, y, w = 100, h = 40) => ({ left: x, top: y, right: x + w, bottom: y + h, width: w, height: h, x, y, toJSON: () => ({}) }); (0, import_vitest.describe)("prefetchIntent", () => { let prefetched, intent; (0, import_vitest.beforeEach)(() => { prefetched = [], intent = (0, import_prefetchIntent.createPrefetchIntent)({ onPrefetch: href => prefetched.push(href) }); }), (0, import_vitest.describe)("basic targeting", () => { (0, import_vitest.it)("prefetches when moving directly toward a link", () => { intent.setRects([{ r: rect(500, 300), h: "/about" }]), intent.move(100, 300, 0, 0), intent.move(140, 300, 40, 0), intent.move(200, 300, 60, 0), (0, import_vitest.expect)(prefetched).toEqual(["/about"]); }), (0, import_vitest.it)("does not prefetch when moving away from a link", () => { intent.setRects([{ r: rect(500, 300), h: "/about" }]), intent.move(400, 300, 0, 0), intent.move(380, 300, -20, 0), intent.move(350, 300, -30, 0), (0, import_vitest.expect)(prefetched).toEqual([]); }), (0, import_vitest.it)("does not prefetch when moving perpendicular to a link", () => { intent.setRects([{ r: rect(500, 300), h: "/about" }]), intent.move(200, 300, 0, 0), intent.move(200, 330, 0, 30), intent.move(200, 370, 0, 40), (0, import_vitest.expect)(prefetched).toEqual([]); }), (0, import_vitest.it)("prefetches link at long distance with good aim", () => { intent.setRects([{ r: rect(500, 280), h: "/far" }]), intent.move(100, 300, 0, 0), intent.move(140, 300, 40, 0), intent.move(200, 300, 60, 0), (0, import_vitest.expect)(prefetched).toEqual(["/far"]); }), (0, import_vitest.it)("does not prefetch when aim is slightly off at long distance", () => { intent.setRects([{ r: rect(600, 300), h: "/far" }]), intent.move(100, 300, 0, 0), intent.move(140, 320, 40, 20), intent.move(200, 360, 60, 40), (0, import_vitest.expect)(prefetched).toEqual([]); }); }), (0, import_vitest.describe)("prevents over-fetching", () => { (0, import_vitest.it)("only prefetches each href once", () => { intent.setRects([{ r: rect(500, 300), h: "/about" }]), intent.move(100, 300, 0, 0), intent.move(140, 300, 40, 0), intent.move(200, 300, 60, 0), (0, import_vitest.expect)(prefetched).toEqual(["/about"]), intent.move(100, 300, -100, 0), intent.move(140, 300, 40, 0), intent.move(200, 300, 60, 0), (0, import_vitest.expect)(prefetched).toEqual(["/about"]); }), (0, import_vitest.it)("removes prefetched links from future consideration", () => { intent.setRects([{ r: rect(400, 300), h: "/first" }, { r: rect(600, 300), h: "/second" }]), intent.move(100, 300, 0, 0), intent.move(150, 300, 50, 0), (0, import_vitest.expect)(prefetched).toEqual(["/first"]), intent.move(250, 300, 100, 0), intent.move(350, 300, 100, 0), (0, import_vitest.expect)(prefetched).toEqual(["/first", "/second"]); }); }), (0, import_vitest.describe)("winner-takes-all with clustered links", () => { (0, import_vitest.it)("only prefetches the best target when multiple links are in path", () => { intent.setRects([{ r: rect(500, 280), h: "/top" }, { r: rect(500, 320), h: "/middle" }, { r: rect(500, 360), h: "/bottom" }]), intent.move(100, 340, 0, 0), intent.move(150, 340, 50, 0), (0, import_vitest.expect)(prefetched.length).toBe(1), (0, import_vitest.expect)(prefetched[0]).toBe("/middle"); }), (0, import_vitest.it)("handles dense nav with 20 links", () => { const links = Array.from({ length: 20 }, (_, i) => ({ r: rect(100 + i * 60, 50, 50, 30), h: `/nav-${i}` })); intent.setRects(links), intent.move(700, 200, 0, 0), intent.move(700, 150, 0, -50), intent.move(700, 100, 0, -50), (0, import_vitest.expect)(prefetched.length).toBe(1), (0, import_vitest.expect)(prefetched[0]).toBe("/nav-10"); }), (0, import_vitest.it)("picks closer link when two are roughly aligned", () => { intent.setRects([{ r: rect(300, 300), h: "/near" }, { r: rect(600, 300), h: "/far" }]), intent.move(100, 320, 0, 0), intent.move(150, 320, 50, 0), (0, import_vitest.expect)(prefetched).toEqual(["/near"]); }), (0, import_vitest.it)("picks better-aimed link over closer link", () => { intent.setRects([{ r: rect(200, 400), h: "/close-but-off" }, // closer but significantly off-axis { r: rect(400, 300), h: "/far-but-aimed" } // farther but dead-on ]), intent.move(100, 300, 0, 0), intent.move(150, 300, 50, 0), (0, import_vitest.expect)(prefetched).toEqual(["/far-but-aimed"]); }); }), (0, import_vitest.describe)("velocity and smoothing", () => { (0, import_vitest.it)("does not prefetch when mouse is stationary", () => { intent.setRects([{ r: rect(500, 300), h: "/about" }]), intent.move(400, 300, 0, 0), intent.move(400, 300, 0, 0), intent.move(400, 300, 0, 0), (0, import_vitest.expect)(prefetched).toEqual([]); }), (0, import_vitest.it)("does not prefetch when mouse is moving slowly", () => { intent.setRects([{ r: rect(500, 300), h: "/about" }]), intent.move(400, 300, 0, 0), intent.move(401, 300, 1, 0), intent.move(402, 300, 1, 0), (0, import_vitest.expect)(prefetched).toEqual([]); }), (0, import_vitest.it)("smooths velocity to avoid jitter false positives", () => { intent.setRects([{ r: rect(500, 300), h: "/about" }]), intent.move(100, 300, 0, 0), intent.move(130, 305, 30, 5), intent.move(160, 298, 30, -7), intent.move(195, 303, 35, 5), (0, import_vitest.expect)(prefetched).toEqual(["/about"]); }); }), (0, import_vitest.describe)("diagonal movement", () => { (0, import_vitest.it)("prefetches with diagonal approach", () => { intent.setRects([{ r: rect(500, 500), h: "/corner" }]), intent.move(200, 200, 0, 0), intent.move(240, 240, 40, 40), intent.move(290, 290, 50, 50), (0, import_vitest.expect)(prefetched).toEqual(["/corner"]); }), (0, import_vitest.it)("handles angled approach to horizontal nav", () => { intent.setRects([{ r: rect(400, 50), h: "/link1" }, { r: rect(500, 50), h: "/link2" }, { r: rect(600, 50), h: "/link3" }]), intent.move(300, 300, 0, 0), intent.move(340, 260, 40, -40), intent.move(390, 210, 50, -50), (0, import_vitest.expect)(prefetched.length).toBe(1); }); }), (0, import_vitest.describe)("edge cases", () => { (0, import_vitest.it)("handles empty rect list", () => { intent.setRects([]), intent.move(100, 100, 0, 0), intent.move(150, 100, 50, 0), (0, import_vitest.expect)(prefetched).toEqual([]); }), (0, import_vitest.it)("handles link at cursor position", () => { intent.setRects([{ r: rect(100, 100), h: "/here" }]), intent.move(150, 120, 0, 0), intent.move(160, 120, 10, 0), (0, import_vitest.expect)(prefetched.length).toBeLessThanOrEqual(1); }), (0, import_vitest.it)("cleans up when observe returns cleanup function", () => { intent.observe({ getBoundingClientRect: () => rect(500, 300) }, "/test")(), intent.move(100, 300, 0, 0), intent.move(150, 300, 50, 0), (0, import_vitest.expect)(prefetched).toEqual([]); }), (0, import_vitest.it)("re-enables prefetch for href after cleanup", () => { const el = { getBoundingClientRect: () => rect(500, 300) }, cleanup = intent.observe(el, "/test"); intent.setRects([{ r: rect(500, 300), h: "/test" }]), intent.move(100, 300, 0, 0), intent.move(140, 300, 40, 0), intent.move(200, 300, 60, 0), (0, import_vitest.expect)(prefetched).toEqual(["/test"]), cleanup(), prefetched.length = 0, intent.observe(el, "/test"), intent.setRects([{ r: rect(500, 300), h: "/test" }]), intent.move(100, 300, 0, 0), intent.move(140, 300, 40, 0), intent.move(200, 300, 60, 0), (0, import_vitest.expect)(prefetched).toEqual(["/test"]); }); }), (0, import_vitest.describe)("reach configuration", () => { (0, import_vitest.it)("respects maxReach option", () => { const shortReach = (0, import_prefetchIntent.createPrefetchIntent)({ onPrefetch: href => prefetched.push(href), maxReach: 200 }); shortReach.setRects([{ r: rect(500, 300), h: "/far" }]), shortReach.move(100, 300, 0, 0), shortReach.move(150, 300, 50, 0), (0, import_vitest.expect)(prefetched).toEqual([]); }), (0, import_vitest.it)("respects perpWeight option for aim strictness", () => { const strictAim = (0, import_prefetchIntent.createPrefetchIntent)({ onPrefetch: href => prefetched.push(href), perpWeight: 10 // very strict }); strictAim.setRects([{ r: rect(400, 350), h: "/off" }]), strictAim.move(100, 300, 0, 0), strictAim.move(150, 300, 50, 0), (0, import_vitest.expect)(prefetched).toEqual([]); }); }), (0, import_vitest.describe)("memory and performance", () => { (0, import_vitest.it)("does not leak elements after cleanup", () => { const cleanups = []; for (let i = 0; i < 100; i++) { const el = {}; cleanups.push(intent.observe(el, `/page-${i}`)); } (0, import_vitest.expect)(intent.nodes.size).toBe(100), cleanups.forEach(c => c()), (0, import_vitest.expect)(intent.nodes.size).toBe(0), (0, import_vitest.expect)(intent.done.size).toBe(0); }), (0, import_vitest.it)("handles rapid observe/unobserve cycles", () => { const el = {}; for (let i = 0; i < 100; i++) intent.observe(el, "/test")(); (0, import_vitest.expect)(intent.nodes.size).toBe(0); }), (0, import_vitest.it)("processes 100 links efficiently", () => { const links = []; for (let row = 0; row < 10; row++) for (let col = 0; col < 10; col++) links.push({ r: rect(100 + col * 80, 100 + row * 50, 60, 30), h: `/link-${row}-${col}` }); intent.setRects(links); const start = performance.now(); for (let i = 0; i < 100; i++) intent.move(50 + i * 5, 300, 5, 0); const elapsed = performance.now() - start; (0, import_vitest.expect)(elapsed).toBeLessThan(50); }), (0, import_vitest.it)("removes prefetched links from rects to speed up future checks", () => { const links = Array.from({ length: 10 }, (_, i) => ({ r: rect(200 + i * 100, 300), h: `/link-${i}` })); intent.setRects(links), intent.move(100, 320, 0, 0), intent.move(150, 320, 50, 0), (0, import_vitest.expect)(prefetched.length).toBe(1), intent.move(100, 320, -50, 0), intent.move(150, 320, 50, 0), intent.move(200, 320, 50, 0), (0, import_vitest.expect)(prefetched.length).toBe(2), (0, import_vitest.expect)(prefetched[0]).not.toBe(prefetched[1]); }); }); });