one
Version:
One is a new React Framework that makes Vite serve both native and web.
217 lines • 11.2 kB
JavaScript
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]);
});
});
});