Merge custom option test fix into widget modernization
This commit is contained in:
21 files changed
+246
-179
No files matched your search
+72
-52
@@ -6,11 +6,7 @@ import { createId } from "@paralleldrive/cuid2";
|
||||
import { stringify as stringifySuperJSON } from "superjson";
|
||||
import { describe, test } from "vitest";
|
||||
|
||||
import {
|
||||
LOGICAL_GRID_CELL_SIZE,
|
||||
LOGICAL_GRID_GAP,
|
||||
MIN_ACCESSIBLE_CANVAS_SCALE,
|
||||
} from "../apps/nextjs/src/components/board/layout/constants";
|
||||
import { LOGICAL_GRID_CELL_SIZE, LOGICAL_GRID_GAP } from "../apps/nextjs/src/components/board/layout/constants";
|
||||
import * as sqliteSchema from "../packages/db/schema/sqlite";
|
||||
import { createHomarrContainer } from "./shared/create-homarr-container";
|
||||
import { createSqliteDbFileAsync } from "./shared/e2e-db";
|
||||
@@ -26,7 +22,6 @@ const gridRuntimeMarker = "homarr-dnd-kit-v1";
|
||||
const dndKitPayloadMarker = "dnd-kit-description";
|
||||
const logicalCellSize = LOGICAL_GRID_CELL_SIZE;
|
||||
const logicalCellPitch = LOGICAL_GRID_CELL_SIZE + LOGICAL_GRID_GAP;
|
||||
const minimumCanvasScale = MIN_ACCESSIBLE_CANVAS_SCALE;
|
||||
|
||||
describe("Board grid", () => {
|
||||
test("supports fixed, nested, rail, pointer, keyboard and reconciliation workflows", async () => {
|
||||
@@ -100,6 +95,11 @@ describe("Board grid", () => {
|
||||
await expect(rail).toBeVisible();
|
||||
await expect(rail).toHaveAttribute("data-board-gutter", "left");
|
||||
await expect(railRootSection).toHaveAttribute("data-rail-placement", "left");
|
||||
await expect(rail).toHaveCSS("overflow-x", "hidden");
|
||||
const initialRailBox = await expectBoundingBoxAsync(rail);
|
||||
const initialRailRootBox = await expectBoundingBoxAsync(railRootSection);
|
||||
expect(initialRailRootBox.width).toBeCloseTo(initialRailBox.width, 1);
|
||||
expect(initialRailRootBox.height).toBeGreaterThanOrEqual(initialRailBox.height - 1);
|
||||
await expect(railItem).toHaveAttribute("data-grid-x", "0");
|
||||
await expect(containerSection).toHaveAttribute("data-grid-w", "2");
|
||||
await expect(containerSection).toHaveAttribute("data-grid-h", "2");
|
||||
@@ -174,21 +174,15 @@ describe("Board grid", () => {
|
||||
expect(await readLayoutShiftScoreAsync(page)).toBeLessThan(0.01);
|
||||
|
||||
// CSS zoom exercises Chromium's browser-zoom layout path without relying
|
||||
// on desktop browser chrome. At 200% the scale floor must preserve
|
||||
// magnification and expose horizontal scrolling instead of shrinking the
|
||||
// board by the inverse zoom factor.
|
||||
// on desktop browser chrome. The board must keep fitting without adding
|
||||
// horizontal scrolling at any effective zoom.
|
||||
await setDocumentZoomAsync(page, 2);
|
||||
await expect(canvas).toHaveAttribute("data-canvas-scale", String(minimumCanvasScale));
|
||||
await expect(canvas).toHaveAttribute("data-canvas-overflow", "true");
|
||||
const zoomedCanvasScale = await readCanvasScaleAsync(canvas);
|
||||
expect(zoomedCanvasScale).toBeLessThan(normalCanvasScale);
|
||||
await expect(canvas).toHaveAttribute("data-canvas-overflow", "false");
|
||||
await expectFixedLogicalTileAsync(logicalTile);
|
||||
await expectUniformVisualScaleAsync(logicalTile);
|
||||
const fittedClockFontSize = await firstItem.locator(".clock-time-text").evaluate((element) => {
|
||||
return Number.parseFloat(getComputedStyle(element).fontSize);
|
||||
});
|
||||
expect(fittedClockFontSize * minimumCanvasScale).toBeGreaterThanOrEqual(25);
|
||||
const zoomedTileBox = await expectBoundingBoxAsync(logicalTile);
|
||||
expect(zoomedTileBox.width).toBeGreaterThan(normalZoomTileBox.width * 1.25);
|
||||
await expectHorizontalOverflowAsync(canvas);
|
||||
await expect(canvas).toHaveCSS("overflow-x", "clip");
|
||||
await expectDocumentNotHorizontallyScrollableAsync(page);
|
||||
const overflowRailY = (await expectBoundingBoxAsync(rail)).y;
|
||||
await page.evaluate(() => window.scrollTo({ top: 300, behavior: "auto" }));
|
||||
@@ -251,6 +245,14 @@ describe("Board grid", () => {
|
||||
await expect(page.locator(`[data-grid-runtime="${gridRuntimeMarker}"]`)).toHaveCount(3);
|
||||
await expect(page.getByTestId("board-grid-editor-loading")).toHaveCount(0);
|
||||
await expect(page.getByTestId("board-canvas-row-count-button")).toHaveCount(0);
|
||||
await expect(rail).toHaveAttribute("data-board-editing", "true");
|
||||
await expect(rail).toHaveCSS("position", "sticky");
|
||||
await expect(rail).toHaveCSS("transform", "none");
|
||||
const railBox = await expectBoundingBoxAsync(rail);
|
||||
expect(railBox.height + railBox.y).toBeCloseTo(page.viewportSize()?.height ?? 0, 0);
|
||||
expect(await rail.evaluate((element) => getComputedStyle(element, "::before").content.replaceAll('"', ""))).toBe(
|
||||
"Left dashboard rail",
|
||||
);
|
||||
await expect(logicalTile).toHaveCSS("overflow-x", "hidden");
|
||||
await expect(logicalTile).toHaveCSS("overflow-y", "auto");
|
||||
await logicalTile.evaluate((element) => {
|
||||
@@ -337,8 +339,11 @@ describe("Board grid", () => {
|
||||
await expect(itemSelectDialog).toHaveCount(0);
|
||||
|
||||
const mainGrid = page.locator(`[data-grid-section-id="${fixture.sectionId}"]`);
|
||||
const editorViewport = page.viewportSize();
|
||||
if (!editorViewport) throw new Error("Expected a browser viewport");
|
||||
expect((await expectBoundingBoxAsync(mainGrid)).height).toBeGreaterThanOrEqual(editorViewport.height - 1);
|
||||
const interactionSnapshot = await readEditorGridSnapshotAsync(mainGrid);
|
||||
await expectGridBoundaryRejectsOverflowAsync(page, mainGrid, firstItem);
|
||||
await expectGridGrowsAtBoundaryAsync(page, canvas, mainGrid, firstItem);
|
||||
await firstItem.locator("[data-grid-item-content]").evaluate((element) => {
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.dataset.e2eDragIframe = "true";
|
||||
@@ -367,8 +372,8 @@ describe("Board grid", () => {
|
||||
.evaluateAll((elements) => elements.forEach((element) => element.remove()));
|
||||
|
||||
await page.setViewportSize({ width: 600, height: 1200 });
|
||||
await expect(canvas).toHaveAttribute("data-canvas-scale", String(minimumCanvasScale));
|
||||
await expectHorizontalOverflowAsync(canvas);
|
||||
await expect(canvas).toHaveAttribute("data-canvas-overflow", "false");
|
||||
await expect(canvas).toHaveCSS("overflow-x", "clip");
|
||||
await expectDocumentNotHorizontallyScrollableAsync(page);
|
||||
for (const direction of ["n", "ne", "e", "se", "s", "sw", "w", "nw"] as const) {
|
||||
await expectTargetSizeAsync(firstItem.locator(`:scope > [data-grid-resize-handle="${direction}"]`));
|
||||
@@ -718,13 +723,12 @@ describe("Board grid", () => {
|
||||
expect(continuousResizeWidth).toBeGreaterThan(logicalCellSize * canvasScale);
|
||||
|
||||
await page.mouse.move(eastResizeStart.x - logicalCellPitch * canvasScale, eastResizeStart.y, { steps: 6 });
|
||||
await expect(containerSection).toHaveAttribute("data-grid-w", "2");
|
||||
await expect(containerSection).toHaveAttribute("data-grid-w", "1");
|
||||
await expect(resizePlaceholder).toHaveAttribute("data-grid-w", "1");
|
||||
expect((await expectBoundingBoxAsync(resizeOutline)).width).toBeCloseTo(logicalCellSize * canvasScale, 1);
|
||||
const liveNestedGridBox = await expectBoundingBoxAsync(nestedEditor);
|
||||
const liveContainerBox = await expectBoundingBoxAsync(containerSection);
|
||||
expect(liveNestedGridBox.width).toBeCloseTo(originalResizeWidth, 1);
|
||||
expect(liveNestedGridBox.width).toBeLessThanOrEqual(liveContainerBox.width + 1);
|
||||
expect(liveContainerBox.width).toBeCloseTo(logicalCellSize * canvasScale, 1);
|
||||
await expect(containerSection.locator(":scope > .board-grid-content-mount")).toHaveCSS("overflow", "clip");
|
||||
await page.mouse.up();
|
||||
await expect(containerSection).toHaveAttribute("data-grid-w", "1");
|
||||
await expect(resizeOutline).toHaveCount(0);
|
||||
@@ -759,15 +763,26 @@ describe("Board grid", () => {
|
||||
await expect(containerSection).toHaveAttribute("data-grid-h", "2");
|
||||
|
||||
const southResizeHandle = containerSection.locator(':scope > [data-grid-resize-handle="s"]');
|
||||
await dragLocatorByAsync(page, southResizeHandle, 0, logicalCellPitch * canvasScale);
|
||||
const gridHeightBeforeSouthResize = (await expectBoundingBoxAsync(mainGrid)).height;
|
||||
const southResizeBox = await expectBoundingBoxAsync(southResizeHandle);
|
||||
const southResizeStart = {
|
||||
x: southResizeBox.x + southResizeBox.width / 2,
|
||||
y: southResizeBox.y + southResizeBox.height / 2,
|
||||
};
|
||||
await page.mouse.move(southResizeStart.x, southResizeStart.y);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(southResizeStart.x, southResizeStart.y + logicalCellPitch * canvasScale, { steps: 8 });
|
||||
await expect(containerSection).toHaveAttribute("data-grid-h", "3");
|
||||
await expect(belowItem).toHaveAttribute("data-grid-y", "3");
|
||||
await expect
|
||||
.poll(async () => (await expectBoundingBoxAsync(mainGrid)).height)
|
||||
.toBeGreaterThanOrEqual(gridHeightBeforeSouthResize);
|
||||
await page.mouse.up();
|
||||
expect(
|
||||
gridLayoutsOverlap(await readGridLayoutAsync(containerSection), await readGridLayoutAsync(belowItem)),
|
||||
).toBe(false);
|
||||
|
||||
// Auto-scroll needs real document overflow. Grow the canvas through an
|
||||
// explicit resize instead of relying on edit mode to add empty rows.
|
||||
// Grow the canvas further to exercise sustained document auto-scroll.
|
||||
await dragLocatorByAsync(page, southResizeHandle, 0, logicalCellPitch * canvasScale * 6);
|
||||
await expect(containerSection).toHaveAttribute("data-grid-h", "9");
|
||||
await expect(belowItem).toHaveAttribute("data-grid-y", "9");
|
||||
@@ -843,14 +858,10 @@ describe("Board grid", () => {
|
||||
false,
|
||||
);
|
||||
|
||||
const stickyRailY = (await expectBoundingBoxAsync(rail)).y;
|
||||
await page.evaluate(() => window.scrollTo({ top: 500, behavior: "auto" }));
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const y = (await expectBoundingBoxAsync(rail)).y;
|
||||
return y >= 0 && y <= stickyRailY + 0.5;
|
||||
})
|
||||
.toBe(true);
|
||||
const stickyRailY = (await expectBoundingBoxAsync(rail)).y;
|
||||
await page.evaluate(() => window.scrollTo({ top: 600, behavior: "auto" }));
|
||||
await expect.poll(async () => (await expectBoundingBoxAsync(rail)).y).toBeCloseTo(stickyRailY, 0);
|
||||
await page.evaluate(() => window.scrollTo({ top: 0, behavior: "auto" }));
|
||||
|
||||
await setDocumentZoomAsync(page, 1.25);
|
||||
@@ -979,6 +990,15 @@ describe("Board grid", () => {
|
||||
const emptyRightGutter = page.getByRole("complementary", { name: "Right dashboard rail" });
|
||||
await expect(emptyRightGutter).toHaveCount(1);
|
||||
expect((await expectBoundingBoxAsync(emptyRightGutter)).width).toBeGreaterThan(0);
|
||||
await page.getByTestId("board-edit-mode-toggle").click();
|
||||
await expect(emptyRightGutter).toHaveAttribute("data-board-editing", "true");
|
||||
expect(
|
||||
await emptyRightGutter.evaluate((element) => ({
|
||||
content: getComputedStyle(element, "::before").content.replaceAll('"', ""),
|
||||
insetInlineStart: getComputedStyle(element, "::before").insetInlineStart,
|
||||
top: getComputedStyle(element, "::before").top,
|
||||
})),
|
||||
).toEqual({ content: "Right dashboard rail", insetInlineStart: "0px", top: "0px" });
|
||||
expect(pageErrors).toEqual([]);
|
||||
} finally {
|
||||
runtimeResources?.stop();
|
||||
@@ -1713,9 +1733,10 @@ const expectImmediateOutsideReleaseRestoresSnapshotAsync = async (page: Page, gr
|
||||
await expect.poll(async () => await readEditorGridSnapshotAsync(grid)).toEqual(snapshot);
|
||||
};
|
||||
|
||||
const expectGridBoundaryRejectsOverflowAsync = async (page: Page, grid: Locator, locator: Locator) => {
|
||||
const expectGridGrowsAtBoundaryAsync = async (page: Page, canvas: Locator, grid: Locator, locator: Locator) => {
|
||||
const snapshot = await readEditorGridSnapshotAsync(grid);
|
||||
const gridBox = await expectBoundingBoxAsync(grid);
|
||||
const canvasBox = await expectBoundingBoxAsync(canvas);
|
||||
const itemBox = await expectBoundingBoxAsync(locator);
|
||||
const start = { x: itemBox.x + itemBox.width * 0.63, y: itemBox.y + itemBox.height * 0.42 };
|
||||
|
||||
@@ -1723,14 +1744,25 @@ const expectGridBoundaryRejectsOverflowAsync = async (page: Page, grid: Locator,
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(start.x + 8, start.y);
|
||||
await expect(locator).toHaveAttribute("data-dnd-drag-source", "true");
|
||||
await page.mouse.move(gridBox.x + gridBox.width / 2, gridBox.y + gridBox.height + logicalCellPitch, { steps: 12 });
|
||||
await expect(page.locator("[data-grid-placeholder-for]")).toHaveCount(0);
|
||||
const activeGridBox = await expectBoundingBoxAsync(grid);
|
||||
expect(activeGridBox.height).toBeCloseTo(gridBox.height, 1);
|
||||
await expect.poll(async () => (await expectBoundingBoxAsync(grid)).height).toBeGreaterThan(gridBox.height);
|
||||
|
||||
await page.mouse.move(gridBox.x + gridBox.width / 2, gridBox.y + gridBox.height + logicalCellPitch / 2, {
|
||||
steps: 12,
|
||||
});
|
||||
await expect(page.locator("[data-grid-placeholder-for]")).toHaveCount(1);
|
||||
const activeGridBox = await expectBoundingBoxAsync(grid);
|
||||
expect(activeGridBox.height).toBeGreaterThan(gridBox.height);
|
||||
|
||||
await page.mouse.move(canvasBox.x + canvasBox.width + 1000, start.y, { steps: 12 });
|
||||
const overlayBox = await expectBoundingBoxAsync(page.locator(".board-grid-drag-overlay[data-dnd-dragging]"));
|
||||
expect(overlayBox.x + overlayBox.width).toBeLessThanOrEqual(canvasBox.x + canvasBox.width + 1);
|
||||
|
||||
await page.keyboard.press("Escape");
|
||||
await page.mouse.up();
|
||||
await expect(page.locator("body")).not.toHaveAttribute("data-board-grid-interacting");
|
||||
await expect.poll(async () => await readEditorGridSnapshotAsync(grid)).toEqual(snapshot);
|
||||
await locator.evaluate((element) => element.scrollIntoView({ block: "center", behavior: "instant" }));
|
||||
await expectGridEntryGeometrySettledAsync(locator);
|
||||
};
|
||||
|
||||
const expectInvalidNestedDropRestoresSnapshotAsync = async (
|
||||
@@ -2036,18 +2068,6 @@ const expectUniformVisualScaleAsync = async (locator: Locator) => {
|
||||
expect(scale.horizontal).toBeCloseTo(scale.vertical, 3);
|
||||
};
|
||||
|
||||
const expectHorizontalOverflowAsync = async (locator: Locator) => {
|
||||
const dimensions = await locator.evaluate((element) => {
|
||||
const htmlElement = element as HTMLElement;
|
||||
return {
|
||||
clientWidth: htmlElement.clientWidth,
|
||||
scrollWidth: htmlElement.scrollWidth,
|
||||
};
|
||||
});
|
||||
|
||||
expect(dimensions.scrollWidth).toBeGreaterThan(dimensions.clientWidth);
|
||||
};
|
||||
|
||||
const expectDocumentNotHorizontallyScrollableAsync = async (page: Page) => {
|
||||
await expect
|
||||
.poll(
|
||||
|
||||
Reference in new issue
Block a user