Skip to content

Commit

Permalink
Fix error boundary for popped out widgets (#1075)
Browse files Browse the repository at this point in the history
* Fixed error boundary for popped out widgets

* Updated error boundary e2e tests

* Updated all e2e tests to use relative paths
  • Loading branch information
MBudreviciusBentley authored Nov 5, 2024
1 parent 515755d commit e0382ef
Show file tree
Hide file tree
Showing 15 changed files with 124 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { createTestFrontstage } from "./createTestFrontstage";
import { ProgressRadial } from "@itwin/itwinui-react";
import { Logger } from "@itwin/core-bentley";
import { loggerCategory } from "../../logger";
import { WidgetContentThrowError } from "@itwin/appui-test-providers";

export const createTestPopoutFrontstage = () => {
{
Expand Down Expand Up @@ -37,6 +38,23 @@ export const createTestPopoutFrontstage = () => {
],
},
},
rightPanel: {
sections: {
start: [
{
id: "error-widget",
label: "Error widget",
canPopout: true,
content: (
<>
<div>Widget content</div>
<WidgetContentThrowError />
</>
),
},
],
},
},
} satisfies Frontstage;
}
};
Expand Down
8 changes: 6 additions & 2 deletions apps/test-providers/src/ui/widgets/LayoutWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -561,15 +561,19 @@ function SelectWidgetControls() {
);
}

function WidgetContentThrowError() {
/**
* Returns a button that throws an error.
* @internal
*/
export function WidgetContentThrowError() {
const [shouldThrow, setShouldThrow] = React.useState(false);
if (shouldThrow) {
throw new Error("Simulated error was thrown.");
}
return (
<>
<h2>Throw error button</h2>
<Button onClick={() => setShouldThrow(true)}>Click Me</Button>
<Button onClick={() => setShouldThrow(true)}>Throw Error</Button>
</>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/appui-react",
"comment": "Added error boundary to poppout widgets.",
"type": "none"
}
],
"packageName": "@itwin/appui-react"
}
4 changes: 2 additions & 2 deletions e2e-tests/tests/backstage/backstage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { expect, test } from "@playwright/test";

test("backstage test", async ({ page, baseURL }) => {
await page.goto(`${baseURL}/blank?frontstageId=widget-api`);
test("backstage test", async ({ page }) => {
await page.goto("./blank?frontstageId=widget-api");
await page.getByRole("button", { name: "Open backstage menu" }).click();

await page.getByRole("menuitem", { name: "Component Examples" }).click();
Expand Down
49 changes: 38 additions & 11 deletions e2e-tests/tests/error-boundaries.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,52 @@
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { expect, Page, test } from "@playwright/test";
import assert from "assert";
import { tabLocator, widgetLocator } from "./Utils";

test.describe("error boundary", () => {
test.beforeEach(async ({ page, baseURL }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=widget-api`);
});
import { expect, test } from "@playwright/test";
import { popoutWidget, tabLocator, widgetLocator } from "./Utils";

test.describe("error boundary tests", () => {
test("should catch an error", async ({ page }) => {
const tab = tabLocator(page, "Layout Controls");
await page.goto("./blank?frontstageId=test-popout");
const tab = tabLocator(page, "Error widget");
const widget = widgetLocator({ tab });

const button = widget.getByRole("button", { name: "Click Me" });
const button = widget.getByRole("button", { name: "Throw Error" });
const error = widget.getByRole("alert");

await expect(error).not.toBeVisible();
await button.click();
await expect(error).toBeVisible();
});

test("should catch an error when widget is popped out and reparentPopoutWidgets is false", async ({
page,
}) => {
await page.goto("./blank?frontstageId=test-popout");
const tab = tabLocator(page, "Error widget");
const widget = widgetLocator({ tab });

const popoutPage = await popoutWidget(widget);
const button = popoutPage.getByRole("button", { name: "Throw Error" });
const error = popoutPage.getByRole("alert");

await expect(error).not.toBeVisible();
await button.click();
await expect(error).toBeVisible();
});

test("should catch an error when widget is popped out and reparentPopoutWidgets is true", async ({
page,
}) => {
await page.goto("./blank?frontstageId=test-popout&reparentPopoutWidgets=1");
const tab = tabLocator(page, "Error widget");
const widget = widgetLocator({ tab });

const popoutPage = await popoutWidget(widget);
const button = popoutPage.getByRole("button", { name: "Throw Error" });
const error = popoutPage.getByRole("alert");

await expect(error).not.toBeVisible();
await button.click();
await expect(error).toBeVisible();
});
});
21 changes: 7 additions & 14 deletions e2e-tests/tests/floating-widget.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { expect, Page, test } from "@playwright/test";
import assert from "assert";
import { expect, test } from "@playwright/test";
import {
dragWidget,
expectSavedFrontstageState,
Expand All @@ -19,9 +18,8 @@ import {
} from "./Utils";

test.describe("floating widget", () => {
test.beforeEach(async ({ page, baseURL }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=widget-api&menu=0`);
test.beforeEach(async ({ page }) => {
await page.goto("./blank?frontstageId=widget-api&menu=0");
});

test("should float a panel section", async ({ page }) => {
Expand Down Expand Up @@ -115,12 +113,8 @@ test.describe("floating widget", () => {
expect(bounds.y).toEqual(initialBounds.y + 30);
});

test("should drag a floating widget (with header)", async ({
page,
baseURL,
}) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=widget-api`);
test("should drag a floating widget (with header)", async ({ page }) => {
await page.goto("./blank?frontstageId=widget-api");

const tab = tabLocator(page, "FW-1");
const widget = widgetLocator({ tab });
Expand Down Expand Up @@ -207,9 +201,8 @@ test.describe("floating widget", () => {
});

test.describe("floating widget send back outline", () => {
test.beforeEach(async ({ page, baseURL }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=widget-api`);
test.beforeEach(async ({ page }) => {
await page.goto("./blank?frontstageId=widget-api");
});

test("should show a widget (with tab) outline", async ({ page }) => {
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/tests/itwinui-v2.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { expect, test } from "@playwright/test";
import { popoutWidget, tabLocator, widgetLocator } from "./Utils";

test("should style in popout", async ({ page }) => {
await page.goto("/blank?frontstageId=itwinui-v2");
await page.goto("./blank?frontstageId=itwinui-v2");

const tab = tabLocator(page, "iTwinUI v2");
const widget = widgetLocator({
Expand Down
20 changes: 7 additions & 13 deletions e2e-tests/tests/popout-widget.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { test, expect } from "@playwright/test";
import assert from "assert";
import {
WidgetState,
expectSavedFrontstageState,
Expand All @@ -18,9 +17,8 @@ import {
} from "./Utils";

test.describe("popout widget", () => {
test.beforeEach(async ({ page, baseURL }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=widget-api`);
test.beforeEach(async ({ page }) => {
await page.goto("./blank?frontstageId=widget-api");
});

test("should popout a widget", async ({ page }) => {
Expand Down Expand Up @@ -192,9 +190,8 @@ test.describe("popout widget", () => {
});
});

test("should copy styles", async ({ baseURL, page }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=test-popout`);
test("should copy styles", async ({ page }) => {
await page.goto("./blank?frontstageId=test-popout");

const tab = tabLocator(page, "Widget 1");
const widget = widgetLocator({ tab });
Expand All @@ -204,11 +201,8 @@ test("should copy styles", async ({ baseURL, page }) => {
await expect(borders).toHaveScreenshot();
});

test("should copy shadow root styles", async ({ baseURL, page }) => {
assert(baseURL);
await page.goto(
`${baseURL}/blank?frontstageId=test-popout&reparentPopoutWidgets=1`
);
test("should copy shadow root styles", async ({ page }) => {
await page.goto("./blank?frontstageId=test-popout&reparentPopoutWidgets=1");

const tab = tabLocator(page, "Widget 1");
const widget = widgetLocator({ tab });
Expand All @@ -223,7 +217,7 @@ test("should render after link styles are loaded", async ({
page,
}) => {
context.route("**", (route) => route.continue());
await page.goto(`/blank?frontstageId=test-popout`);
await page.goto("./blank?frontstageId=test-popout");

const tab = tabLocator(page, "Widget 1");
const widget = widgetLocator({ tab });
Expand Down
31 changes: 12 additions & 19 deletions e2e-tests/tests/stage-panel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { Locator, expect, test } from "@playwright/test";
import assert from "assert";
import {
StagePanelState,
panelLocator,
Expand All @@ -12,9 +11,8 @@ import {
} from "./Utils";

test.describe("WidgetApi", () => {
test.beforeEach(async ({ page, baseURL }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=widget-api`);
test.beforeEach(async ({ page }) => {
await page.goto("./blank?frontstageId=widget-api");
});

test("should toggle pin state", async ({ page }) => {
Expand Down Expand Up @@ -63,28 +61,25 @@ test.describe("WidgetApi", () => {
});
});

test("should initialize defaults", async ({ baseURL, page }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=test-panel`);
test("should initialize defaults", async ({ page }) => {
await page.goto("./blank?frontstageId=test-panel");

const panel = panelLocator({ page, side: "left" });
await expect(panel).toBeVisible();
expect(await getPanelSize(panel)).toBe(200);
});

test("should initialize size", async ({ baseURL, page }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=test-panel&size=500`);
test("should initialize size", async ({ page }) => {
await page.goto("./blank?frontstageId=test-panel&size=500");

const panel = panelLocator({ page, side: "left" });
await expect(panel).toBeVisible();
expect(await getPanelSize(panel)).toBe(500);
});

test("should initialize minimized", async ({ baseURL, page }) => {
assert(baseURL);
test("should initialize minimized", async ({ page }) => {
await page.goto(
`${baseURL}/blank?frontstageId=test-panel&defaultState=${StagePanelState.Minimized}`
`./blank?frontstageId=test-panel&defaultState=${StagePanelState.Minimized}`
);

const panel = panelLocator({ page, side: "left" });
Expand All @@ -93,18 +88,16 @@ test("should initialize minimized", async ({ baseURL, page }) => {
expect(await getPanelSize(panel)).toBe(0);
});

test("should initialize resizable", async ({ baseURL, page }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=test-panel&resizable=0`);
test("should initialize resizable", async ({ page }) => {
await page.goto("./blank?frontstageId=test-panel&resizable=0");

const panel = panelLocator({ page, side: "left" });
const handle = handleLocator(panel);
await expect(handle).not.toBeVisible();
});

test("should resize (single panel)", async ({ baseURL, page }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=test-panel&menu=0`);
test("should resize (single panel)", async ({ page }) => {
await page.goto("./blank?frontstageId=test-panel&menu=0");

const panel = panelLocator({ page, side: "left" });
const handle = handleLocator(panel);
Expand Down
22 changes: 6 additions & 16 deletions e2e-tests/tests/tool-settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { expect, test } from "@playwright/test";
import assert from "assert";
import {
expectSavedFrontstageState,
frontstageLocator,
Expand All @@ -14,9 +13,8 @@ import {
} from "./Utils";

test.describe("tool settings", () => {
test.beforeEach(async ({ page, baseURL }) => {
assert(baseURL);
await page.goto(`${baseURL}/blank?frontstageId=widget-api`);
test.beforeEach(async ({ page }) => {
await page.goto("./blank?frontstageId=widget-api");
});

test("should render tool settings", async ({ page }) => {
Expand All @@ -25,23 +23,17 @@ test.describe("tool settings", () => {

test("should not render tool settings (undefined tool settings config)", async ({
page,
baseURL,
}) => {
await page.goto(
`${baseURL}/blank?frontstageId=widget-api&toolSettings=off`
);
await page.goto("./blank?frontstageId=widget-api&toolSettings=off");
await expect(
page.getByText("does not have tool settings.")
).not.toBeVisible();
});

test("should not render tool settings (hidden tool settings)", async ({
page,
baseURL,
}) => {
await page.goto(
`${baseURL}/blank?frontstageId=widget-api&toolSettings=hidden`
);
await page.goto("./blank?frontstageId=widget-api&toolSettings=hidden");
await expect(
page.getByText("does not have tool settings.")
).not.toBeVisible();
Expand All @@ -57,10 +49,8 @@ test.describe("tool settings", () => {
await expect(page.getByText("does not have tool settings.")).toBeVisible();
});

test("should show/hide (hidden tool settings)", async ({ page, baseURL }) => {
await page.goto(
`${baseURL}/blank?frontstageId=widget-api&toolSettings=hidden`
);
test("should show/hide (hidden tool settings)", async ({ page }) => {
await page.goto("./blank?frontstageId=widget-api&toolSettings=hidden");
await expect(
page.getByText("does not have tool settings.")
).not.toBeVisible();
Expand Down
Loading

0 comments on commit e0382ef

Please sign in to comment.