Source code
Revision control
Copy as Markdown
Other Tools
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
import { act, renderHook } from "@testing-library/react";
import { useAppendPinDrop } from "content-src/components/TopSites/useAppendPinDrop.jsx";
// A placeholder ref whose rect contains (50, 50) but not (500, 500).
const placeholderAt = () => ({
getBoundingClientRect: () => ({ left: 0, right: 100, top: 0, bottom: 100 }),
});
const inside = { clientX: 50, clientY: 50, preventDefault: jest.fn() };
const outside = { clientX: 500, clientY: 500, preventDefault: jest.fn() };
const pinned = url => ({ url, isPinned: true });
const frecent = url => ({ url });
function setup(props = {}) {
const onDragEvent = jest.fn();
const { result, rerender } = renderHook(
args =>
useAppendPinDrop({
baseSites: [],
draggedSite: null,
isMovable: () => true,
onDragEvent,
previewActive: false,
...args,
}),
{ initialProps: props }
);
return { result, rerender, onDragEvent };
}
describe("useAppendPinDrop", () => {
it("reserves the slot just after the contiguous pinned block", () => {
const { result } = setup({
baseSites: [
],
draggedSite: dragged,
});
// lastPinIndex is 1, so the append slot is 2.
expect(result.current.decorations.zeroPinSlot).toBe(2);
});
it("opens the slot at 0 when there are no pins", () => {
const { result } = setup({
draggedSite: dragged,
});
expect(result.current.decorations.zeroPinSlot).toBe(0);
});
it("keeps the slot closed when nothing is being dragged", () => {
const { result } = setup({
draggedSite: null,
});
expect(result.current.decorations.zeroPinSlot).toBe(-1);
});
it("keeps the slot closed when the dragged site is not movable", () => {
const { result } = setup({
draggedSite: dragged,
isMovable: () => false,
});
expect(result.current.decorations.zeroPinSlot).toBe(-1);
});
it("hides the reserved slot while the reflow preview drives", () => {
const { result } = setup({
draggedSite: dragged,
previewActive: true,
});
expect(result.current.decorations.zeroPinSlot).toBe(-1);
});
it("collapses the dragged source out of the grid", () => {
const { result } = setup({
baseSites: [siteA, dragged],
draggedSite: dragged,
});
expect(result.current.sites[0]).toBe(siteA);
expect(result.current.sites[1]).toEqual({ ...dragged, isCollapsed: true });
});
it("returns baseSites untouched when disabled", () => {
const { result } = setup({ baseSites, draggedSite: null });
expect(result.current.sites).toBe(baseSites);
});
it("commits at the append slot when dropped on the placeholder", () => {
const { result, onDragEvent } = setup({
draggedSite: dragged,
});
result.current.decorations.setZeroPinRef(placeholderAt());
act(() => result.current.listProps.onDrop(inside));
// appendIndex is lastPinIndex(0) + 1 = 1.
expect(onDragEvent).toHaveBeenCalledWith(inside, 1);
});
it("does not commit when the drop lands outside the placeholder", () => {
const { result, onDragEvent } = setup({
draggedSite: dragged,
});
result.current.decorations.setZeroPinRef(placeholderAt());
act(() => result.current.listProps.onDrop(outside));
expect(onDragEvent).not.toHaveBeenCalled();
});
it("clears the reflow and marks over when dragging onto the slot", () => {
const { result, onDragEvent } = setup({
draggedSite: dragged,
});
result.current.decorations.setZeroPinRef(placeholderAt());
act(() => result.current.listProps.onDragOver(inside));
// A no-index dragover clears useTopSitesDnD's insert-anywhere reflow.
expect(onDragEvent).toHaveBeenCalledWith(inside);
expect(result.current.decorations.overZeroPin).toBe(true);
});
it("does not claim a dragover that misses the slot", () => {
const { result, onDragEvent } = setup({
draggedSite: dragged,
});
result.current.decorations.setZeroPinRef(placeholderAt());
act(() => result.current.listProps.onDragOver(outside));
expect(onDragEvent).not.toHaveBeenCalled();
expect(result.current.decorations.overZeroPin).toBe(false);
});
});