From 87e361a1ba9d88e9f81e9a0445bd3f07b505b7ac Mon Sep 17 00:00:00 2001 From: Brian Vaughn Date: Sun, 1 Feb 2026 09:14:16 -0500 Subject: [PATCH] Simplify e2e test main window vs popup window logic --- integrations/tests/playwright.config.ts | 16 +- integrations/tests/src/utils/goToUrl.ts | 9 +- integrations/tests/tests/cursor.spec.tsx | 447 ++++--- .../tests/tests/default-sizes.spec.tsx | 136 +-- .../tests/tests/fixed-size-elements.spec.tsx | 300 +++-- .../tests/tests/imperative-api-hooks.spec.tsx | 94 +- .../tests/keyboard-interactions.spec.tsx | 619 +++++----- .../tests/tests/pointer-interactions.spec.tsx | 1025 ++++++++--------- .../tests/tests/resize-events.spec.tsx | 413 ++++--- .../tests/tests/stacking-order.spec.tsx | 265 ++--- integrations/tests/tests/visibility.spec.tsx | 160 ++- 11 files changed, 1697 insertions(+), 1787 deletions(-) diff --git a/integrations/tests/playwright.config.ts b/integrations/tests/playwright.config.ts index 3516f6cab..ea7f0e10c 100644 --- a/integrations/tests/playwright.config.ts +++ b/integrations/tests/playwright.config.ts @@ -1,9 +1,23 @@ import { defineConfig, devices } from "@playwright/test"; +export type ExtendedUseOptions = { + usePopUpWindow: boolean; +}; + const DEVICES = [ { name: "chromium", - use: devices["Desktop Chrome"] + use: { + ...devices["Desktop Chrome"], + usePopUpWindow: false + } + }, + { + name: "chromium: popup", + use: { + ...devices["Desktop Chrome"], + usePopUpWindow: true + } } ]; diff --git a/integrations/tests/src/utils/goToUrl.ts b/integrations/tests/src/utils/goToUrl.ts index 983680c43..fde97d88f 100644 --- a/integrations/tests/src/utils/goToUrl.ts +++ b/integrations/tests/src/utils/goToUrl.ts @@ -1,5 +1,7 @@ import type { Page } from "@playwright/test"; +import { test } from "@playwright/test"; import { createElement, type ReactElement } from "react"; +import type { ExtendedUseOptions } from "../../playwright.config"; import { PopupWindow } from "../../src/components/PopupWindow"; import { encode } from "./serializer/encode"; @@ -11,15 +13,16 @@ export async function goToUrl( useGroupRef?: boolean | undefined; usePanelCallbackRef?: boolean | undefined; usePanelRef?: boolean | undefined; - usePopUpWindow?: boolean | undefined; } = {} ): Promise { + const usePopUpWindow = (test.info().project.use as ExtendedUseOptions) + .usePopUpWindow; + const { useGroupCallbackRef = false, useGroupRef = false, usePanelCallbackRef = false, - usePanelRef = false, - usePopUpWindow = false + usePanelRef = false } = config; let element = elementProp; diff --git a/integrations/tests/tests/cursor.spec.tsx b/integrations/tests/tests/cursor.spec.tsx index faf69dd48..994ecc2fd 100644 --- a/integrations/tests/tests/cursor.spec.tsx +++ b/integrations/tests/tests/cursor.spec.tsx @@ -10,237 +10,228 @@ import { goToUrl } from "../src/utils/goToUrl"; const moveConfig = { steps: 10 }; test.describe("cursor", () => { - for (const usePopUpWindow of [true, false]) { - test.describe(usePopUpWindow ? "in a popup" : "in the main window", () => { - test("horizontal", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, + test("horizontal", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + const hitAreaBox = await calculateHitArea(page, ["left", "right"]); + const { x, y } = getCenterCoordinates(hitAreaBox); + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("auto"); + + await page.mouse.move(x, y, moveConfig); + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("ew-resize"); + + await page.mouse.down(); + await page.mouse.move(25, y, moveConfig); + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("e-resize"); + + await page.mouse.move(975, y, moveConfig); + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("w-resize"); + }); + + test("vertical", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + const hitAreaBox = await calculateHitArea(page, ["top", "bottom"]); + const { x, y } = getCenterCoordinates(hitAreaBox); + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("auto"); + + await page.mouse.move(x, y, moveConfig); + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("ns-resize"); + + await page.mouse.down(); + await page.mouse.move(x, 0, moveConfig); + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("s-resize"); + + await page.mouse.move(x, 600, moveConfig); + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("n-resize"); + }); + + test("intersecting", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + - , - { usePopUpWindow } - ); - - const hitAreaBox = await calculateHitArea(page, ["left", "right"]); - const { x, y } = getCenterCoordinates(hitAreaBox); - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("auto"); - - await page.mouse.move(x, y, moveConfig); - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("ew-resize"); - - await page.mouse.down(); - await page.mouse.move(25, y, moveConfig); - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("e-resize"); - - await page.mouse.move(975, y, moveConfig); - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("w-resize"); - }); - - test("vertical", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); - - const hitAreaBox = await calculateHitArea(page, ["top", "bottom"]); - const { x, y } = getCenterCoordinates(hitAreaBox); - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("auto"); - - await page.mouse.move(x, y, moveConfig); - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("ns-resize"); - - await page.mouse.down(); - await page.mouse.move(x, 0, moveConfig); - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("s-resize"); - - await page.mouse.move(x, 600, moveConfig); - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("n-resize"); - }); - - test("intersecting", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - - - - - - - , - { usePopUpWindow } - ); - - const separator = page.getByTestId("vertical-separator"); - const boundingBox = (await separator.boundingBox())!; - const x = boundingBox.x + boundingBox.width / 2; - const y = boundingBox.y; - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("auto"); - - // Centered - await page.mouse.move(x, y, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("move"); - - // Top left - await page.mouse.down(); - await page.mouse.move(1, 1, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("se-resize"); - - // Top - await page.mouse.move(x, 0, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("s-resize"); - - // Top right - await page.mouse.move(1000, 1, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("sw-resize"); - - // Right - await page.mouse.move(975, y, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("w-resize"); - - // Bottom right - await page.mouse.move(1000, 600, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("nw-resize"); - - // Bottom - await page.mouse.move(x, 600, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("n-resize"); - - // Bottom left - await page.mouse.move(1, 600, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("ne-resize"); - - // Left - await page.mouse.move(25, y, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("e-resize"); - - // Centered - await page.mouse.move(x, y, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("move"); - }); - - test("edge case", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - - - - - - - , - { usePopUpWindow } - ); - - const separator = page.getByTestId("vertical-separator"); - const boundingBox = (await separator.boundingBox())!; - const x = boundingBox.x + boundingBox.width / 2; - const y = boundingBox.y; - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("auto"); - - // Centered - await page.mouse.move(x, y, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("move"); - - await page.mouse.down(); - - // Moving only in one dimension should not affect the cursor - await page.mouse.move(x, y - 25, moveConfig); - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("move"); - }); - - test("disabled", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - + + + + ); + + const separator = page.getByTestId("vertical-separator"); + const boundingBox = (await separator.boundingBox())!; + const x = boundingBox.x + boundingBox.width / 2; + const y = boundingBox.y; + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("auto"); + + // Centered + await page.mouse.move(x, y, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("move"); + + // Top left + await page.mouse.down(); + await page.mouse.move(1, 1, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("se-resize"); + + // Top + await page.mouse.move(x, 0, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("s-resize"); + + // Top right + await page.mouse.move(1000, 1, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("sw-resize"); + + // Right + await page.mouse.move(975, y, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("w-resize"); + + // Bottom right + await page.mouse.move(1000, 600, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("nw-resize"); + + // Bottom + await page.mouse.move(x, 600, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("n-resize"); + + // Bottom left + await page.mouse.move(1, 600, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("ne-resize"); + + // Left + await page.mouse.move(25, y, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("e-resize"); + + // Centered + await page.mouse.move(x, y, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("move"); + }); + + test("edge case", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + - - , - { usePopUpWindow } - ); - - const hitAreaBox = await calculateHitArea(page, ["top", "bottom"]); - const { x, y } = getCenterCoordinates(hitAreaBox); - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("auto"); - - await page.mouse.move(x, y, moveConfig); - - expect( - await page.evaluate(() => getComputedStyle(document.body).cursor) - ).toBe("auto"); - }); - }); - } + + + + + ); + + const separator = page.getByTestId("vertical-separator"); + const boundingBox = (await separator.boundingBox())!; + const x = boundingBox.x + boundingBox.width / 2; + const y = boundingBox.y; + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("auto"); + + // Centered + await page.mouse.move(x, y, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("move"); + + await page.mouse.down(); + + // Moving only in one dimension should not affect the cursor + await page.mouse.move(x, y - 25, moveConfig); + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("move"); + }); + + test("disabled", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + const hitAreaBox = await calculateHitArea(page, ["top", "bottom"]); + const { x, y } = getCenterCoordinates(hitAreaBox); + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("auto"); + + await page.mouse.move(x, y, moveConfig); + + expect( + await page.evaluate(() => getComputedStyle(document.body).cursor) + ).toBe("auto"); + }); }); diff --git a/integrations/tests/tests/default-sizes.spec.tsx b/integrations/tests/tests/default-sizes.spec.tsx index 998534f1f..eebfa618d 100644 --- a/integrations/tests/tests/default-sizes.spec.tsx +++ b/integrations/tests/tests/default-sizes.spec.tsx @@ -4,83 +4,75 @@ import { goToUrl } from "../src/utils/goToUrl"; // High level tests; more nuanced scenarios are covered by unit tests test.describe("default panel sizes", () => { - for (const usePopUpWindow of [true, false]) { - test.describe(usePopUpWindow ? "in a popup" : "in the main window", () => { - test("percentages", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - - - , - { usePopUpWindow } - ); + test("percentages", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + + + ); - await expect(page.getByText("id: left")).toContainText("30%"); - await expect(page.getByText("id: middle")).toContainText("0%"); - await expect(page.getByText("id: right")).toContainText("70%"); - await expect(page.getByRole("separator")).toHaveCount(2); - }); + await expect(page.getByText("id: left")).toContainText("30%"); + await expect(page.getByText("id: middle")).toContainText("0%"); + await expect(page.getByText("id: right")).toContainText("70%"); + await expect(page.getByRole("separator")).toHaveCount(2); + }); - test("pixels", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - - - , - { usePopUpWindow } - ); + test("pixels", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + + + ); - await expect(page.getByText("id: left")).toContainText("200px"); - await expect(page.getByText("id: middle")).toContainText("0px"); - await expect(page.getByText("id: right")).toContainText("752px"); - await expect(page.getByRole("separator")).toHaveCount(2); - }); + await expect(page.getByText("id: left")).toContainText("200px"); + await expect(page.getByText("id: middle")).toContainText("0px"); + await expect(page.getByText("id: right")).toContainText("752px"); + await expect(page.getByRole("separator")).toHaveCount(2); + }); - test("rems", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - - - , - { usePopUpWindow } - ); + test("rems", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + + + ); - await expect(page.getByText("id: left")).toContainText("160px"); - await expect(page.getByText("id: middle")).toContainText("0px"); - await expect(page.getByText("id: right")).toContainText("792px"); - await expect(page.getByRole("separator")).toHaveCount(2); - }); + await expect(page.getByText("id: left")).toContainText("160px"); + await expect(page.getByText("id: middle")).toContainText("0px"); + await expect(page.getByText("id: right")).toContainText("792px"); + await expect(page.getByRole("separator")).toHaveCount(2); + }); - test("vw", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - - - , - { usePopUpWindow } - ); + test("vw", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + + + ); - await expect(page.getByText("id: left")).toContainText("250px"); - await expect(page.getByText("id: middle")).toContainText("0px"); - await expect(page.getByText("id: right")).toContainText("702px"); - await expect(page.getByRole("separator")).toHaveCount(2); - }); - }); - } + await expect(page.getByText("id: left")).toContainText("250px"); + await expect(page.getByText("id: middle")).toContainText("0px"); + await expect(page.getByText("id: right")).toContainText("702px"); + await expect(page.getByRole("separator")).toHaveCount(2); + }); }); diff --git a/integrations/tests/tests/fixed-size-elements.spec.tsx b/integrations/tests/tests/fixed-size-elements.spec.tsx index 2acf9b3d6..6a8b03b67 100644 --- a/integrations/tests/tests/fixed-size-elements.spec.tsx +++ b/integrations/tests/tests/fixed-size-elements.spec.tsx @@ -4,160 +4,148 @@ import { assertLayoutChangeCounts } from "../src/utils/assertLayoutChangeCounts" import { goToUrl } from "../src/utils/goToUrl"; test.describe("fixed size elements", () => { - for (const usePopUpWindow of [true, false]) { - test.describe(usePopUpWindow ? "in a popup" : "in the main window", () => { - test("should not be interactive directly", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - -
foo+bar
- - -
bar+baz
- - -
baz+qux
- - -
, - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - - for (const text of ["foo+bar", "bar+baz", "baz+qux"]) { - const box = (await page.getByText(text).boundingBox())!; - - await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); - await page.mouse.down(); - await page.mouse.move(0, 0); - await page.mouse.up(); - } - - await assertLayoutChangeCounts(mainPage, 1); - }); - - test("should work without an explicit separator", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - -
foo+bar
- - -
bar+baz
- - -
baz+qux
- - -
, - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - - const boxFoo = (await page.getByText("id: foo").boundingBox())!; - - await page.mouse.move(boxFoo.x + boxFoo.width, boxFoo.y); - await page.mouse.down(); - await page.mouse.move(0, 0); - await page.mouse.up(); - - await assertLayoutChangeCounts(mainPage, 2); - - const boxBar = (await page.getByText("id: bar").boundingBox())!; - - await page.mouse.move(boxBar.x, boxBar.y); - await page.mouse.down(); - await page.mouse.move(1000, 0); - await page.mouse.up(); - - await assertLayoutChangeCounts(mainPage, 3); - }); - - test("should work with an explicit separator", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - -
foo+bar
- - -
bar+baz
- - -
baz+qux
- - -
, - { usePopUpWindow } - ); - - const separatorBox = (await page.getByTestId("bar+baz").boundingBox())!; - - await page.mouse.move(separatorBox.x, separatorBox.y); - await page.mouse.down(); - await page.mouse.move(0, 0); - await page.mouse.up(); - - await assertLayoutChangeCounts(mainPage, 2); - - const boxBaz = (await page.getByText("id: baz").boundingBox())!; - - await page.mouse.move(boxBaz.x, boxBaz.y); - await page.mouse.down(); - await page.mouse.move(1000, 0); - await page.mouse.up(); - - await assertLayoutChangeCounts(mainPage, 3); - }); - - test("should work with two explicit separators", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - -
foo+bar
- - -
bar+baz
- - -
baz+qux
- - -
, - { usePopUpWindow } - ); - - let separatorBox = (await page - .getByTestId("baz+qux+left") - .boundingBox())!; - - await page.mouse.move(separatorBox.x, separatorBox.y); - await page.mouse.down(); - await page.mouse.move(0, 0); - await page.mouse.up(); - - await assertLayoutChangeCounts(mainPage, 2); - - separatorBox = (await page.getByTestId("baz+qux+right").boundingBox())!; - - await page.mouse.move(separatorBox.x, separatorBox.y); - await page.mouse.down(); - await page.mouse.move(1000, 0); - await page.mouse.up(); - - await assertLayoutChangeCounts(mainPage, 3); - }); - }); - } + test("should not be interactive directly", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + +
foo+bar
+ + +
bar+baz
+ + +
baz+qux
+ + +
+ ); + + await assertLayoutChangeCounts(mainPage, 1); + + for (const text of ["foo+bar", "bar+baz", "baz+qux"]) { + const box = (await page.getByText(text).boundingBox())!; + + await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await page.mouse.down(); + await page.mouse.move(0, 0); + await page.mouse.up(); + } + + await assertLayoutChangeCounts(mainPage, 1); + }); + + test("should work without an explicit separator", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + +
foo+bar
+ + +
bar+baz
+ + +
baz+qux
+ + +
+ ); + + await assertLayoutChangeCounts(mainPage, 1); + + const boxFoo = (await page.getByText("id: foo").boundingBox())!; + + await page.mouse.move(boxFoo.x + boxFoo.width, boxFoo.y); + await page.mouse.down(); + await page.mouse.move(0, 0); + await page.mouse.up(); + + await assertLayoutChangeCounts(mainPage, 2); + + const boxBar = (await page.getByText("id: bar").boundingBox())!; + + await page.mouse.move(boxBar.x, boxBar.y); + await page.mouse.down(); + await page.mouse.move(1000, 0); + await page.mouse.up(); + + await assertLayoutChangeCounts(mainPage, 3); + }); + + test("should work with an explicit separator", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + +
foo+bar
+ + +
bar+baz
+ + +
baz+qux
+ + +
+ ); + + const separatorBox = (await page.getByTestId("bar+baz").boundingBox())!; + + await page.mouse.move(separatorBox.x, separatorBox.y); + await page.mouse.down(); + await page.mouse.move(0, 0); + await page.mouse.up(); + + await assertLayoutChangeCounts(mainPage, 2); + + const boxBaz = (await page.getByText("id: baz").boundingBox())!; + + await page.mouse.move(boxBaz.x, boxBaz.y); + await page.mouse.down(); + await page.mouse.move(1000, 0); + await page.mouse.up(); + + await assertLayoutChangeCounts(mainPage, 3); + }); + + test("should work with two explicit separators", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + +
foo+bar
+ + +
bar+baz
+ + +
baz+qux
+ + +
+ ); + + let separatorBox = (await page.getByTestId("baz+qux+left").boundingBox())!; + + await page.mouse.move(separatorBox.x, separatorBox.y); + await page.mouse.down(); + await page.mouse.move(0, 0); + await page.mouse.up(); + + await assertLayoutChangeCounts(mainPage, 2); + + separatorBox = (await page.getByTestId("baz+qux+right").boundingBox())!; + + await page.mouse.move(separatorBox.x, separatorBox.y); + await page.mouse.down(); + await page.mouse.move(1000, 0); + await page.mouse.up(); + + await assertLayoutChangeCounts(mainPage, 3); + }); }); diff --git a/integrations/tests/tests/imperative-api-hooks.spec.tsx b/integrations/tests/tests/imperative-api-hooks.spec.tsx index 0bd28f514..21f4d11ee 100644 --- a/integrations/tests/tests/imperative-api-hooks.spec.tsx +++ b/integrations/tests/tests/imperative-api-hooks.spec.tsx @@ -4,59 +4,55 @@ import { goToUrl } from "../src/utils/goToUrl"; // High level tests; more nuanced scenarios are covered by unit tests test.describe("imperative API hooks", () => { - for (const usePopUpWindow of [true, false]) { - test.describe(usePopUpWindow ? "in a popup" : "in the main window", () => { - for (const { useGroupCallbackRef, useGroupRef } of [ - { useGroupRef: true }, - { useGroupCallbackRef: true } - ]) { - test.describe( - useGroupCallbackRef ? "useGroupCallbackRef" : "useGroupRef", - () => { - test("should work", async ({ page: mainPage }) => { - await goToUrl( - mainPage, - - - - - , - { usePopUpWindow, useGroupCallbackRef, useGroupRef } - ); + for (const { useGroupCallbackRef, useGroupRef } of [ + { useGroupRef: true }, + { useGroupCallbackRef: true } + ]) { + test.describe( + useGroupCallbackRef ? "useGroupCallbackRef" : "useGroupRef", + () => { + test("should work", async ({ page: mainPage }) => { + await goToUrl( + mainPage, + + + + + , + { useGroupCallbackRef, useGroupRef } + ); - await expect( - mainPage.getByText("imperativeGroupApiLayout") - ).toContainText('"left": 30'); - }); - } - ); + await expect( + mainPage.getByText("imperativeGroupApiLayout") + ).toContainText('"left": 30'); + }); } + ); + } - for (const { usePanelCallbackRef, usePanelRef } of [ - { usePanelRef: true }, - { usePanelCallbackRef: true } - ]) { - test.describe( - usePanelCallbackRef ? "usePanelCallbackRef" : "usePanelRef", - () => { - test("should work", async ({ page: mainPage }) => { - await goToUrl( - mainPage, - - - - - , - { usePopUpWindow, usePanelCallbackRef, usePanelRef } - ); + for (const { usePanelCallbackRef, usePanelRef } of [ + { usePanelRef: true }, + { usePanelCallbackRef: true } + ]) { + test.describe( + usePanelCallbackRef ? "usePanelCallbackRef" : "usePanelRef", + () => { + test("should work", async ({ page: mainPage }) => { + await goToUrl( + mainPage, + + + + + , + { usePanelCallbackRef, usePanelRef } + ); - await expect( - mainPage.getByText("imperativePanelApiSize") - ).toContainText('"asPercentage": 70'); - }); - } - ); + await expect( + mainPage.getByText("imperativePanelApiSize") + ).toContainText('"asPercentage": 70'); + }); } - }); + ); } }); diff --git a/integrations/tests/tests/keyboard-interactions.spec.tsx b/integrations/tests/tests/keyboard-interactions.spec.tsx index c383e4778..37b0f0d35 100644 --- a/integrations/tests/tests/keyboard-interactions.spec.tsx +++ b/integrations/tests/tests/keyboard-interactions.spec.tsx @@ -9,321 +9,308 @@ import { goToUrl } from "../src/utils/goToUrl"; // - https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/separator_role test.describe("keyboard interactions: window splitter api", () => { - for (const usePopUpWindow of [true, false]) { - test.describe(usePopUpWindow ? "in a popup" : "in the main window", () => { - test("horizontal: arrow keys", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "left", - "aria-valuemax": "95", - "aria-valuemin": "5", - "aria-valuenow": "30" - }); - - const separator = page.getByRole("separator"); - - await separator.focus(); - await page.keyboard.press("ArrowLeft"); - - await assertLayoutChangeCounts(mainPage, 2); - await expect(mainPage.getByText('"left": 25')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "left", - "aria-valuemax": "95", - "aria-valuemin": "5", - "aria-valuenow": "25" - }); - - await page.keyboard.press("ArrowRight"); - - await assertLayoutChangeCounts(mainPage, 3); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "left", - "aria-valuemax": "95", - "aria-valuemin": "5", - "aria-valuenow": "30" - }); - - // Up/down are no-ops - await page.keyboard.press("ArrowUp"); - await page.keyboard.press("ArrowDown"); - await assertLayoutChangeCounts(mainPage, 3); - }); - - test("vertical: arrow keys", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"top": 30')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "top", - "aria-valuemax": "95", - "aria-valuemin": "5", - "aria-valuenow": "30" - }); - - const separator = page.getByRole("separator"); - - await separator.focus(); - await page.keyboard.press("ArrowDown"); - - await assertLayoutChangeCounts(mainPage, 2); - await expect(mainPage.getByText('"top": 35')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "top", - "aria-valuemax": "95", - "aria-valuemin": "5", - "aria-valuenow": "35" - }); - - await page.keyboard.press("ArrowUp"); - - await assertLayoutChangeCounts(mainPage, 3); - await expect(mainPage.getByText('"top": 30')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "top", - "aria-valuemax": "95", - "aria-valuemin": "5", - "aria-valuenow": "30" - }); - - // Left/right are no-ops - await page.keyboard.press("ArrowLeft"); - await page.keyboard.press("ArrowRight"); - await assertLayoutChangeCounts(mainPage, 3); - }); - - test("enter key and collapsible panel", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 50')).toBeVisible(); - await expect(mainPage.getByText('"right": 50')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "left", - "aria-valuemax": "80", - "aria-valuemin": "5", - "aria-valuenow": "50" - }); - - const separator = page.getByRole("separator"); - await separator.focus(); - await page.keyboard.press("Enter"); - - await assertLayoutChangeCounts(mainPage, 2); - await expect(mainPage.getByText('"left": 5')).toBeVisible(); - await expect(mainPage.getByText('"right": 95')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "left", - "aria-valuemax": "80", - "aria-valuemin": "5", - "aria-valuenow": "5" - }); - - await page.keyboard.press("Enter"); - - await assertLayoutChangeCounts(mainPage, 3); - await expect(mainPage.getByText('"left": 50')).toBeVisible(); - await expect(mainPage.getByText('"right": 50')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "left", - "aria-valuemax": "80", - "aria-valuemin": "5", - "aria-valuenow": "50" - }); - - await page.keyboard.press("ArrowLeft"); - - await assertLayoutChangeCounts(mainPage, 4); - await expect(mainPage.getByText('"left": 45')).toBeVisible(); - await expect(mainPage.getByText('"right": 55')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "left", - "aria-valuemax": "80", - "aria-valuemin": "5", - "aria-valuenow": "45" - }); - - await page.keyboard.press("Enter"); - - await assertLayoutChangeCounts(mainPage, 5); - await expect(mainPage.getByText('"left": 5')).toBeVisible(); - await expect(mainPage.getByText('"right": 95')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "left", - "aria-valuemax": "80", - "aria-valuemin": "5", - "aria-valuenow": "5" - }); - - await page.keyboard.press("Enter"); - - await assertLayoutChangeCounts(mainPage, 6); - await expect(mainPage.getByText('"left": 45')).toBeVisible(); - await expect(mainPage.getByText('"right": 55')).toBeVisible(); - - await expect(await getSeparatorAriaAttributes(page)).toEqual({ - "aria-controls": "left", - "aria-valuemax": "80", - "aria-valuemin": "5", - "aria-valuenow": "45" - }); - }); - - test("enter key and non-collapsible panel", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 50')).toBeVisible(); - await expect(mainPage.getByText('"right": 50')).toBeVisible(); - - const separator = page.getByRole("separator"); - await separator.focus(); - await page.keyboard.press("Enter"); - - await assertLayoutChangeCounts(mainPage, 1); - }); - - test("home and end keys", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 50')).toBeVisible(); - await expect(mainPage.getByText('"right": 50')).toBeVisible(); - - const separator = page.getByRole("separator"); - - await separator.focus(); - await page.keyboard.press("Home"); - - await assertLayoutChangeCounts(mainPage, 2); - await expect(mainPage.getByText('"left": 20')).toBeVisible(); - await expect(mainPage.getByText('"right": 80')).toBeVisible(); - - await page.keyboard.press("End"); - - await assertLayoutChangeCounts(mainPage, 3); - await expect(mainPage.getByText('"left": 95')).toBeVisible(); - await expect(mainPage.getByText('"right": 5')).toBeVisible(); - }); - - test("f6 key cycles through separators", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - -
- - - , - { usePopUpWindow } - ); - - await page.getByTestId("separator-left").focus(); - await expect(page.getByTestId("separator-left")).toBeFocused(); - - await page.keyboard.press("F6"); - await expect(page.getByTestId("separator-center")).toBeFocused(); - - await page.keyboard.press("F6"); - await expect(page.getByTestId("separator-right")).toBeFocused(); - - await page.keyboard.press("F6"); - await expect(page.getByTestId("separator-left")).toBeFocused(); - - await page.keyboard.press("Shift+F6"); - await expect(page.getByTestId("separator-right")).toBeFocused(); - }); - - test("should respect when parent Group has been disabled", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - - const separator = page.getByRole("separator"); - - await separator.focus(); - await page.keyboard.press("ArrowLeft"); - await page.keyboard.press("ArrowRight"); - await page.keyboard.press("ArrowUp"); - await page.keyboard.press("ArrowDown"); - await page.keyboard.press("Enter"); - await page.keyboard.press("Home"); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - }); + test("horizontal: arrow keys", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "left", + "aria-valuemax": "95", + "aria-valuemin": "5", + "aria-valuenow": "30" }); - } + + const separator = page.getByRole("separator"); + + await separator.focus(); + await page.keyboard.press("ArrowLeft"); + + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"left": 25')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "left", + "aria-valuemax": "95", + "aria-valuemin": "5", + "aria-valuenow": "25" + }); + + await page.keyboard.press("ArrowRight"); + + await assertLayoutChangeCounts(mainPage, 3); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "left", + "aria-valuemax": "95", + "aria-valuemin": "5", + "aria-valuenow": "30" + }); + + // Up/down are no-ops + await page.keyboard.press("ArrowUp"); + await page.keyboard.press("ArrowDown"); + await assertLayoutChangeCounts(mainPage, 3); + }); + + test("vertical: arrow keys", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"top": 30')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "top", + "aria-valuemax": "95", + "aria-valuemin": "5", + "aria-valuenow": "30" + }); + + const separator = page.getByRole("separator"); + + await separator.focus(); + await page.keyboard.press("ArrowDown"); + + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"top": 35')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "top", + "aria-valuemax": "95", + "aria-valuemin": "5", + "aria-valuenow": "35" + }); + + await page.keyboard.press("ArrowUp"); + + await assertLayoutChangeCounts(mainPage, 3); + await expect(mainPage.getByText('"top": 30')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "top", + "aria-valuemax": "95", + "aria-valuemin": "5", + "aria-valuenow": "30" + }); + + // Left/right are no-ops + await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowRight"); + await assertLayoutChangeCounts(mainPage, 3); + }); + + test("enter key and collapsible panel", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 50')).toBeVisible(); + await expect(mainPage.getByText('"right": 50')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "left", + "aria-valuemax": "80", + "aria-valuemin": "5", + "aria-valuenow": "50" + }); + + const separator = page.getByRole("separator"); + await separator.focus(); + await page.keyboard.press("Enter"); + + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"left": 5')).toBeVisible(); + await expect(mainPage.getByText('"right": 95')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "left", + "aria-valuemax": "80", + "aria-valuemin": "5", + "aria-valuenow": "5" + }); + + await page.keyboard.press("Enter"); + + await assertLayoutChangeCounts(mainPage, 3); + await expect(mainPage.getByText('"left": 50')).toBeVisible(); + await expect(mainPage.getByText('"right": 50')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "left", + "aria-valuemax": "80", + "aria-valuemin": "5", + "aria-valuenow": "50" + }); + + await page.keyboard.press("ArrowLeft"); + + await assertLayoutChangeCounts(mainPage, 4); + await expect(mainPage.getByText('"left": 45')).toBeVisible(); + await expect(mainPage.getByText('"right": 55')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "left", + "aria-valuemax": "80", + "aria-valuemin": "5", + "aria-valuenow": "45" + }); + + await page.keyboard.press("Enter"); + + await assertLayoutChangeCounts(mainPage, 5); + await expect(mainPage.getByText('"left": 5')).toBeVisible(); + await expect(mainPage.getByText('"right": 95')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "left", + "aria-valuemax": "80", + "aria-valuemin": "5", + "aria-valuenow": "5" + }); + + await page.keyboard.press("Enter"); + + await assertLayoutChangeCounts(mainPage, 6); + await expect(mainPage.getByText('"left": 45')).toBeVisible(); + await expect(mainPage.getByText('"right": 55')).toBeVisible(); + + await expect(await getSeparatorAriaAttributes(page)).toEqual({ + "aria-controls": "left", + "aria-valuemax": "80", + "aria-valuemin": "5", + "aria-valuenow": "45" + }); + }); + + test("enter key and non-collapsible panel", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 50')).toBeVisible(); + await expect(mainPage.getByText('"right": 50')).toBeVisible(); + + const separator = page.getByRole("separator"); + await separator.focus(); + await page.keyboard.press("Enter"); + + await assertLayoutChangeCounts(mainPage, 1); + }); + + test("home and end keys", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 50')).toBeVisible(); + await expect(mainPage.getByText('"right": 50')).toBeVisible(); + + const separator = page.getByRole("separator"); + + await separator.focus(); + await page.keyboard.press("Home"); + + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"left": 20')).toBeVisible(); + await expect(mainPage.getByText('"right": 80')).toBeVisible(); + + await page.keyboard.press("End"); + + await assertLayoutChangeCounts(mainPage, 3); + await expect(mainPage.getByText('"left": 95')).toBeVisible(); + await expect(mainPage.getByText('"right": 5')).toBeVisible(); + }); + + test("f6 key cycles through separators", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + +
+ + + + ); + + await page.getByTestId("separator-left").focus(); + await expect(page.getByTestId("separator-left")).toBeFocused(); + + await page.keyboard.press("F6"); + await expect(page.getByTestId("separator-center")).toBeFocused(); + + await page.keyboard.press("F6"); + await expect(page.getByTestId("separator-right")).toBeFocused(); + + await page.keyboard.press("F6"); + await expect(page.getByTestId("separator-left")).toBeFocused(); + + await page.keyboard.press("Shift+F6"); + await expect(page.getByTestId("separator-right")).toBeFocused(); + }); + + test("should respect when parent Group has been disabled", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + + const separator = page.getByRole("separator"); + + await separator.focus(); + await page.keyboard.press("ArrowLeft"); + await page.keyboard.press("ArrowRight"); + await page.keyboard.press("ArrowUp"); + await page.keyboard.press("ArrowDown"); + await page.keyboard.press("Enter"); + await page.keyboard.press("Home"); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + }); }); diff --git a/integrations/tests/tests/pointer-interactions.spec.tsx b/integrations/tests/tests/pointer-interactions.spec.tsx index 2c612999b..f314353ac 100644 --- a/integrations/tests/tests/pointer-interactions.spec.tsx +++ b/integrations/tests/tests/pointer-interactions.spec.tsx @@ -10,558 +10,525 @@ import { goToUrl } from "../src/utils/goToUrl"; import { resizeHelper } from "../src/utils/pointer-interactions/resizeHelper"; test.describe("pointer interactions", () => { - for (const usePopUpWindow of [true, false]) { - test.describe(usePopUpWindow ? "in a popup" : "in the main window", () => { - test("drag separator to resize group", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - - await resizeHelper(page, ["left", "right"], 100, 0); - - await assertLayoutChangeCounts(mainPage, 2); - await expect(mainPage.getByText('"left": 40')).toBeVisible(); - - await resizeHelper(page, ["left", "right"], 1000, 0); + test("drag separator to resize group", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], 100, 0); + + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"left": 40')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], 1000, 0); + + await assertLayoutChangeCounts(mainPage, 3); + await expect(mainPage.getByText('"left": 95')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], -1000, 0); + + await assertLayoutChangeCounts(mainPage, 4); + await expect(mainPage.getByText('"left": 5')).toBeVisible(); + }); - await assertLayoutChangeCounts(mainPage, 3); - await expect(mainPage.getByText('"left": 95')).toBeVisible(); - - await resizeHelper(page, ["left", "right"], -1000, 0); + // See github.com/bvaughn/react-resizable-panels/issues/581 + test("should not handle or call preventDefault for right-click events", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + + const separator = page.getByRole("separator"); + const box = (await separator.boundingBox())!; + + await page.mouse.move(box.x, box.y); + await page.mouse.down({ button: "right" }); + + // Right-click should have been registered but not handled by this library + await expect(separator).not.toHaveAttribute("data-separator", "active"); + + // Subsequent clicks should not impact the layout + await page.mouse.move(box.x - 100, box.y); + await page.mouse.click(0, 0); + + await new Promise((resolve) => setTimeout(resolve, 100)); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + }); + + // See github.com/bvaughn/react-resizable-panels/issues/594 + test("should only call preventDefault for pointerup events that were handled by this library", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + + + + ); + + const clickable = page.getByText("Clickable"); + const clickableBox = (await clickable.boundingBox())!; + + // A handled "pointerdown" event should also prevent the corresponding "pointerup" event + + const separator = page.getByRole("separator"); + const separatorBox = (await separator.boundingBox())!; + + await page.mouse.move(separatorBox.x, separatorBox.y + separatorBox.height); + await page.mouse.down(); + await page.mouse.move(clickableBox.x, clickableBox.y); + await page.mouse.up(); + + await expect(page.getByText("Clickable down:0 up:0")).toBeVisible(); + + // An unhandled "pointerdown" event should not prevent the corresponding "pointerup" event + + await page.mouse.move(1000, 0); + await page.mouse.down(); + await page.mouse.move(clickableBox.x, clickableBox.y); + await page.mouse.up(); + + await expect(page.getByText("Clickable down:0 up:1")).toBeVisible(); + }); + + test("drag panel boundary to resize group", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], 100, 0); + + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"left": 40')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], 1000, 0); + + await assertLayoutChangeCounts(mainPage, 3); + await expect(mainPage.getByText('"left": 95')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], -1000, 0); + + await assertLayoutChangeCounts(mainPage, 4); + await expect(mainPage.getByText('"left": 5')).toBeVisible(); + }); + + test("should not resize when disabled", async ({ page: mainPage }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], 100, 0); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + }); + + test("dragging a handle should resize multiple panels", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 33')).toBeVisible(); + await expect(mainPage.getByText('"center": 33')).toBeVisible(); + await expect(mainPage.getByText('"right": 33')).toBeVisible(); + + await resizeHelper(page, ["left", "center"], 1000, 0); + + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"left": 89')).toBeVisible(); + await expect(mainPage.getByText('"center": 5')).toBeVisible(); + await expect(mainPage.getByText('"right": 5')).toBeVisible(); + + await resizeHelper(page, ["center", "right"], -1000, 0); + + await assertLayoutChangeCounts(mainPage, 3); + await expect(mainPage.getByText('"left": 5')).toBeVisible(); + await expect(mainPage.getByText('"center": 5')).toBeVisible(); + await expect(mainPage.getByText('"right": 89')).toBeVisible(); + + await resizeHelper(page, ["center", "right"], 1000, 0); + + await assertLayoutChangeCounts(mainPage, 4); + await expect(mainPage.getByText('"left": 5')).toBeVisible(); + await expect(mainPage.getByText('"center": 89')).toBeVisible(); + await expect(mainPage.getByText('"right": 5')).toBeVisible(); + }); + + test("initial position should be the anchor while a drag is in progress", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1, 1); + await expect(mainPage.getByText('"left": 33')).toBeVisible(); + await expect(mainPage.getByText('"center": 33')).toBeVisible(); + await expect(mainPage.getByText('"right": 33')).toBeVisible(); + + const hitAreaBox = await calculateHitArea(page, ["left", "center"]); + const { x, y } = getCenterCoordinates(hitAreaBox); + + await page.mouse.move(x, y); + await page.mouse.down(); + await page.mouse.move(x + 500, y, { + steps: 1 + }); - await assertLayoutChangeCounts(mainPage, 4); - await expect(mainPage.getByText('"left": 5')).toBeVisible(); - }); + await assertLayoutChangeCounts(mainPage, 2, 1); + await expect(mainPage.getByText('"left": 86')).toBeVisible(); + await expect(mainPage.getByText('"center": 5')).toBeVisible(); + await expect(mainPage.getByText('"right": 9')).toBeVisible(); + + await page.mouse.move(x - 500, y); + + await assertLayoutChangeCounts(mainPage, 3, 1); + await expect(mainPage.getByText('"left": 5')).toBeVisible(); + await expect(mainPage.getByText('"center": 61')).toBeVisible(); + await expect(mainPage.getByText('"right": 33')).toBeVisible(); + + await page.mouse.move(x, y); + await page.mouse.up(); + + // TRICKY Because the cursor ended at the exact same starting spot, no layout change was committed + await assertLayoutChangeCounts(mainPage, 4, 1); + await expect(mainPage.getByText('"left": 33')).toBeVisible(); + await expect(mainPage.getByText('"center": 33')).toBeVisible(); + await expect(mainPage.getByText('"right": 33')).toBeVisible(); + }); + + test("drag should measure delta using the available group size (minus flex gap)", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 10')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], 800, 0); + + // The new layout would be ~91% and 9% if not for flex-gap + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"left": 95')).toBeVisible(); + }); + + test("should disable pointer-events while resize is active", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + const hitAreaBox = await calculateHitArea(page, ["left", "right"]); + const { x, y } = getCenterCoordinates(hitAreaBox); + + const panel = page.getByText("id: left"); + + await page.mouse.move(x, y); + await expect(panel).toHaveCSS("pointer-events", "auto"); + + await page.mouse.down(); + await expect(panel).toHaveCSS("pointer-events", "none"); + + await page.mouse.up(); + await expect(panel).toHaveCSS("pointer-events", "auto"); + }); + + test("does not notify on-changed handler until pointer resize finishes", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1, 1); + await expect(mainPage.getByText('"left": 33')).toBeVisible(); + await expect(mainPage.getByText('"center": 33')).toBeVisible(); + await expect(mainPage.getByText('"right": 33')).toBeVisible(); + + const hitAreaBox = await calculateHitArea(page, ["left", "center"]); + const { x, y } = getCenterCoordinates(hitAreaBox); + + await page.mouse.move(x, y); + await page.mouse.down(); + await page.mouse.move(x + 500, y, { + steps: 1 + }); - // See github.com/bvaughn/react-resizable-panels/issues/581 - test("should not handle or call preventDefault for right-click events", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); + await assertLayoutChangeCounts(mainPage, 2, 1); + await expect(mainPage.getByText('"left": 86')).toBeVisible(); + await expect(mainPage.getByText('"center": 5')).toBeVisible(); + await expect(mainPage.getByText('"right": 9')).toBeVisible(); + + await page.mouse.move(x - 500, y); + + await assertLayoutChangeCounts(mainPage, 3, 1); + await expect(mainPage.getByText('"left": 5')).toBeVisible(); + await expect(mainPage.getByText('"center": 61')).toBeVisible(); + await expect(mainPage.getByText('"right": 33')).toBeVisible(); + + await page.mouse.up(); + + await assertLayoutChangeCounts(mainPage, 3, 2); + await expect(mainPage.getByText('"left": 5')).toBeVisible(); + await expect(mainPage.getByText('"center": 61')).toBeVisible(); + await expect(mainPage.getByText('"right": 33')).toBeVisible(); + }); + + test("double-clicking a separator resets primary panel to default size", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], 100, 0); + + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"left": 40')).toBeVisible(); + + const separator = page.getByTestId("separator"); + await separator.dblclick(); + + await assertLayoutChangeCounts(mainPage, 3); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + }); + + test("double-clicking a separator resets secondary panel to default size", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + ); + + await assertLayoutChangeCounts(mainPage, 1); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + + await resizeHelper(page, ["left", "right"], 100, 0); + + await assertLayoutChangeCounts(mainPage, 2); + await expect(mainPage.getByText('"left": 40')).toBeVisible(); + + const separator = page.getByTestId("separator"); + await separator.dblclick(); + + await assertLayoutChangeCounts(mainPage, 3); + await expect(mainPage.getByText('"left": 30')).toBeVisible(); + }); + + test.describe("focus", () => { + test("should update focus to the nearest separator", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + + + + + + ); + + const hitAreaBox = await calculateHitArea(page, ["center", "right"]); + const { x, y } = getCenterCoordinates(hitAreaBox); + + const separator = page.getByTestId("separator-right"); + await expect(separator).not.toBeFocused(); + + await page.mouse.move(x, y); + await page.mouse.down(); + await page.mouse.up(); + + await expect(separator).toBeFocused(); + }); - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - - const separator = page.getByRole("separator"); - const box = (await separator.boundingBox())!; - - await page.mouse.move(box.x, box.y); - await page.mouse.down({ button: "right" }); - - // Right-click should have been registered but not handled by this library - await expect(separator).not.toHaveAttribute("data-separator", "active"); - - // Subsequent clicks should not impact the layout - await page.mouse.move(box.x - 100, box.y); - await page.mouse.click(0, 0); - - await new Promise((resolve) => setTimeout(resolve, 100)); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - }); - - // See github.com/bvaughn/react-resizable-panels/issues/594 - test("should only call preventDefault for pointerup events that were handled by this library", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - - - - , - { usePopUpWindow } + test("should activate the closest separator in the event that there is two", async ({ + page: mainPage + }) => { + const page = await goToUrl( + mainPage, + + + +
fixed size
+ + +
+ ); + + const separatorLeft = page.getByTestId("separator-left"); + const separatorRight = page.getByTestId("separator-right"); + + { + const { x, y } = getCenterCoordinates( + (await separatorRight.boundingBox())! ); - const clickable = page.getByText("Clickable"); - const clickableBox = (await clickable.boundingBox())!; - - // A handled "pointerdown" event should also prevent the corresponding "pointerup" event - - const separator = page.getByRole("separator"); - const separatorBox = (await separator.boundingBox())!; + await expect(separatorRight).not.toBeFocused(); - await page.mouse.move( - separatorBox.x, - separatorBox.y + separatorBox.height + await page.mouse.move(x, y); + await expect(separatorLeft).not.toHaveAttribute( + "data-separator", + "hover" ); - await page.mouse.down(); - await page.mouse.move(clickableBox.x, clickableBox.y); - await page.mouse.up(); - - await expect(page.getByText("Clickable down:0 up:0")).toBeVisible(); + await expect(separatorRight).toHaveAttribute("data-separator", "hover"); - // An unhandled "pointerdown" event should not prevent the corresponding "pointerup" event - - await page.mouse.move(1000, 0); await page.mouse.down(); - await page.mouse.move(clickableBox.x, clickableBox.y); - await page.mouse.up(); - - await expect(page.getByText("Clickable down:0 up:1")).toBeVisible(); - }); - - test("drag panel boundary to resize group", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - , - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - - await resizeHelper(page, ["left", "right"], 100, 0); - - await assertLayoutChangeCounts(mainPage, 2); - await expect(mainPage.getByText('"left": 40')).toBeVisible(); - - await resizeHelper(page, ["left", "right"], 1000, 0); - - await assertLayoutChangeCounts(mainPage, 3); - await expect(mainPage.getByText('"left": 95')).toBeVisible(); - - await resizeHelper(page, ["left", "right"], -1000, 0); - - await assertLayoutChangeCounts(mainPage, 4); - await expect(mainPage.getByText('"left": 5')).toBeVisible(); - }); - - test("should not resize when disabled", async ({ page: mainPage }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } + await expect(separatorLeft).not.toHaveAttribute( + "data-separator", + "active" ); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - - await resizeHelper(page, ["left", "right"], 100, 0); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - }); - - test("dragging a handle should resize multiple panels", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - - - , - { usePopUpWindow } + await expect(separatorRight).toHaveAttribute( + "data-separator", + "active" ); - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 33')).toBeVisible(); - await expect(mainPage.getByText('"center": 33')).toBeVisible(); - await expect(mainPage.getByText('"right": 33')).toBeVisible(); - - await resizeHelper(page, ["left", "center"], 1000, 0); - - await assertLayoutChangeCounts(mainPage, 2); - await expect(mainPage.getByText('"left": 89')).toBeVisible(); - await expect(mainPage.getByText('"center": 5')).toBeVisible(); - await expect(mainPage.getByText('"right": 5')).toBeVisible(); - - await resizeHelper(page, ["center", "right"], -1000, 0); - - await assertLayoutChangeCounts(mainPage, 3); - await expect(mainPage.getByText('"left": 5')).toBeVisible(); - await expect(mainPage.getByText('"center": 5')).toBeVisible(); - await expect(mainPage.getByText('"right": 89')).toBeVisible(); - - await resizeHelper(page, ["center", "right"], 1000, 0); - - await assertLayoutChangeCounts(mainPage, 4); - await expect(mainPage.getByText('"left": 5')).toBeVisible(); - await expect(mainPage.getByText('"center": 89')).toBeVisible(); - await expect(mainPage.getByText('"right": 5')).toBeVisible(); - }); - - test("initial position should be the anchor while a drag is in progress", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - - - , - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1, 1); - await expect(mainPage.getByText('"left": 33')).toBeVisible(); - await expect(mainPage.getByText('"center": 33')).toBeVisible(); - await expect(mainPage.getByText('"right": 33')).toBeVisible(); - - const hitAreaBox = await calculateHitArea(page, ["left", "center"]); - const { x, y } = getCenterCoordinates(hitAreaBox); - - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.move(x + 500, y, { - steps: 1 - }); - - await assertLayoutChangeCounts(mainPage, 2, 1); - await expect(mainPage.getByText('"left": 86')).toBeVisible(); - await expect(mainPage.getByText('"center": 5')).toBeVisible(); - await expect(mainPage.getByText('"right": 9')).toBeVisible(); - - await page.mouse.move(x - 500, y); - - await assertLayoutChangeCounts(mainPage, 3, 1); - await expect(mainPage.getByText('"left": 5')).toBeVisible(); - await expect(mainPage.getByText('"center": 61')).toBeVisible(); - await expect(mainPage.getByText('"right": 33')).toBeVisible(); - - await page.mouse.move(x, y); await page.mouse.up(); + await expect(separatorRight).toBeFocused(); + } - // TRICKY Because the cursor ended at the exact same starting spot, no layout change was committed - await assertLayoutChangeCounts(mainPage, 4, 1); - await expect(mainPage.getByText('"left": 33')).toBeVisible(); - await expect(mainPage.getByText('"center": 33')).toBeVisible(); - await expect(mainPage.getByText('"right": 33')).toBeVisible(); - }); - - test("drag should measure delta using the available group size (minus flex gap)", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } + { + const { x, y } = getCenterCoordinates( + (await separatorLeft.boundingBox())! ); - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 10')).toBeVisible(); - - await resizeHelper(page, ["left", "right"], 800, 0); - - // The new layout would be ~91% and 9% if not for flex-gap - await assertLayoutChangeCounts(mainPage, 2); - await expect(mainPage.getByText('"left": 95')).toBeVisible(); - }); - - test("should disable pointer-events while resize is active", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); - - const hitAreaBox = await calculateHitArea(page, ["left", "right"]); - const { x, y } = getCenterCoordinates(hitAreaBox); - - const panel = page.getByText("id: left"); + await expect(separatorLeft).not.toBeFocused(); await page.mouse.move(x, y); - await expect(panel).toHaveCSS("pointer-events", "auto"); - - await page.mouse.down(); - await expect(panel).toHaveCSS("pointer-events", "none"); - - await page.mouse.up(); - await expect(panel).toHaveCSS("pointer-events", "auto"); - }); - - test("does not notify on-changed handler until pointer resize finishes", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - - - , - { usePopUpWindow } + await expect(separatorLeft).toHaveAttribute("data-separator", "hover"); + await expect(separatorRight).not.toHaveAttribute( + "data-separator", + "hover" ); - await assertLayoutChangeCounts(mainPage, 1, 1); - await expect(mainPage.getByText('"left": 33')).toBeVisible(); - await expect(mainPage.getByText('"center": 33')).toBeVisible(); - await expect(mainPage.getByText('"right": 33')).toBeVisible(); - - const hitAreaBox = await calculateHitArea(page, ["left", "center"]); - const { x, y } = getCenterCoordinates(hitAreaBox); - - await page.mouse.move(x, y); await page.mouse.down(); - await page.mouse.move(x + 500, y, { - steps: 1 - }); - - await assertLayoutChangeCounts(mainPage, 2, 1); - await expect(mainPage.getByText('"left": 86')).toBeVisible(); - await expect(mainPage.getByText('"center": 5')).toBeVisible(); - await expect(mainPage.getByText('"right": 9')).toBeVisible(); - - await page.mouse.move(x - 500, y); - - await assertLayoutChangeCounts(mainPage, 3, 1); - await expect(mainPage.getByText('"left": 5')).toBeVisible(); - await expect(mainPage.getByText('"center": 61')).toBeVisible(); - await expect(mainPage.getByText('"right": 33')).toBeVisible(); - - await page.mouse.up(); - - await assertLayoutChangeCounts(mainPage, 3, 2); - await expect(mainPage.getByText('"left": 5')).toBeVisible(); - await expect(mainPage.getByText('"center": 61')).toBeVisible(); - await expect(mainPage.getByText('"right": 33')).toBeVisible(); - }); - - test("double-clicking a separator resets primary panel to default size", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } - ); - - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - - await resizeHelper(page, ["left", "right"], 100, 0); - - await assertLayoutChangeCounts(mainPage, 2); - await expect(mainPage.getByText('"left": 40')).toBeVisible(); - - const separator = page.getByTestId("separator"); - await separator.dblclick(); - - await assertLayoutChangeCounts(mainPage, 3); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - }); - - test("double-clicking a separator resets secondary panel to default size", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - , - { usePopUpWindow } + await expect(separatorLeft).toHaveAttribute("data-separator", "active"); + await expect(separatorRight).not.toHaveAttribute( + "data-separator", + "active" ); - await assertLayoutChangeCounts(mainPage, 1); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - - await resizeHelper(page, ["left", "right"], 100, 0); - - await assertLayoutChangeCounts(mainPage, 2); - await expect(mainPage.getByText('"left": 40')).toBeVisible(); - - const separator = page.getByTestId("separator"); - await separator.dblclick(); - - await assertLayoutChangeCounts(mainPage, 3); - await expect(mainPage.getByText('"left": 30')).toBeVisible(); - }); - - test.describe("focus", () => { - test("should update focus to the nearest separator", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - - - - - , - { usePopUpWindow } - ); - - const hitAreaBox = await calculateHitArea(page, ["center", "right"]); - const { x, y } = getCenterCoordinates(hitAreaBox); - - const separator = page.getByTestId("separator-right"); - await expect(separator).not.toBeFocused(); - - await page.mouse.move(x, y); - await page.mouse.down(); - await page.mouse.up(); - - await expect(separator).toBeFocused(); - }); - - test("should activate the closest separator in the event that there is two", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - - -
fixed size
- - -
, - { usePopUpWindow } - ); - - const separatorLeft = page.getByTestId("separator-left"); - const separatorRight = page.getByTestId("separator-right"); - - { - const { x, y } = getCenterCoordinates( - (await separatorRight.boundingBox())! - ); - - await expect(separatorRight).not.toBeFocused(); - - await page.mouse.move(x, y); - await expect(separatorLeft).not.toHaveAttribute( - "data-separator", - "hover" - ); - await expect(separatorRight).toHaveAttribute( - "data-separator", - "hover" - ); - - await page.mouse.down(); - await expect(separatorLeft).not.toHaveAttribute( - "data-separator", - "active" - ); - await expect(separatorRight).toHaveAttribute( - "data-separator", - "active" - ); - - await page.mouse.up(); - await expect(separatorRight).toBeFocused(); - } - - { - const { x, y } = getCenterCoordinates( - (await separatorLeft.boundingBox())! - ); - - await expect(separatorLeft).not.toBeFocused(); - - await page.mouse.move(x, y); - await expect(separatorLeft).toHaveAttribute( - "data-separator", - "hover" - ); - await expect(separatorRight).not.toHaveAttribute( - "data-separator", - "hover" - ); - - await page.mouse.down(); - await expect(separatorLeft).toHaveAttribute( - "data-separator", - "active" - ); - await expect(separatorRight).not.toHaveAttribute( - "data-separator", - "active" - ); - - await page.mouse.up(); - await expect(separatorLeft).toBeFocused(); - } - }); - }); - - // See github.com/bvaughn/react-resizable-panels/issues/645 - test("should update separator state when the cursor mouses over an iframe", async ({ - page: mainPage - }) => { - const page = await goToUrl( - mainPage, - - -