Add unit test for the toggle component

Using playwright-provided chromium via vitest + testing library
This commit is contained in:
David Baker
2026-03-16 18:51:40 +00:00
parent 6bc2bb6ab9
commit dd2a125d52
5 changed files with 144 additions and 1 deletions
@@ -14,9 +14,13 @@
"devDependencies": {
"@arcmantle/vite-plugin-import-css-sheet": "^1.0.12",
"@element-hq/element-web-module-api": "^1.0.0",
"@testing-library/dom": "^10.4.1",
"@testing-library/react": "^16.3.2",
"@testing-library/user-event": "^14.6.1",
"@types/node": "^22.10.7",
"@types/react": "^19",
"@vitejs/plugin-react": "^5.0.0",
"@vitest/browser-playwright": "4.0.18",
"@vitest/coverage-v8": "^4.0.0",
"react": "^19",
"rollup-plugin-external-globals": "^0.13.0",
@@ -0,0 +1,13 @@
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { afterEach } from "vitest";
import { cleanup } from "@testing-library/react";
afterEach(() => {
cleanup();
});
@@ -0,0 +1,111 @@
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { beforeEach, describe, expect, test, vi } from "vitest";
import { render, screen } from "@testing-library/react";
import { type WidgetApi, type I18nApi } from "@element-hq/element-web-module-api";
import { type IWidget } from "matrix-widget-api";
import { type PropsWithChildren } from "react";
import { TooltipProvider } from "@vector-im/compound-web";
import userEvent from "@testing-library/user-event";
import { WidgetToggle } from "../src/toggle";
const roomId = "!room:example.com";
const mockWidget = (overrides: Partial<IWidget> = {}): IWidget => ({
id: "widget-1",
creatorUserId: "@user:example.com",
type: "m.custom",
name: "My Widget",
url: "https://example.com",
...overrides,
});
const mockWidgetApi = (overrides: Partial<WidgetApi> = {}): WidgetApi =>
({
getAppAvatarUrl: vi.fn().mockReturnValue(null),
isAppInContainer: vi.fn().mockReturnValue(false),
moveAppToContainer: vi.fn(),
...overrides,
}) as unknown as WidgetApi;
const mockI18nApi = (): I18nApi =>
({
translate: vi.fn().mockImplementation((key: string, vars?: Record<string, string>) => {
let result = key;
if (vars) {
for (const [k, v] of Object.entries(vars)) {
result = result.replace(`%(${k})s`, v);
}
}
return result;
}),
}) as unknown as I18nApi;
const wrapper = ({ children }: PropsWithChildren): React.JSX.Element => <TooltipProvider>{children}</TooltipProvider>;
describe("WidgetToggle", () => {
let widgetApi: WidgetApi;
let i18nApi: I18nApi;
let app: IWidget;
beforeEach(() => {
widgetApi = mockWidgetApi();
i18nApi = mockI18nApi();
app = mockWidget();
});
test("displays avatar image when widget has an avatar URL", () => {
(widgetApi.getAppAvatarUrl as ReturnType<typeof vi.fn>).mockReturnValue("https://example.com/avatar.png");
render(<WidgetToggle app={app} roomId={roomId} widgetApi={widgetApi} i18nApi={i18nApi} />, { wrapper });
const img = screen.getByRole("img", { name: app.name });
expect(img).toBeDefined();
expect(img.getAttribute("src")).toBe("https://example.com/avatar.png");
});
test("renders the Jitsi avatar for Jitsi widgets", () => {
app = mockWidget({ type: "m.jitsi", name: "Jitsi" });
render(<WidgetToggle app={app} roomId={roomId} widgetApi={widgetApi} i18nApi={i18nApi} />, { wrapper });
const img = screen.getByRole("img", { name: "Jitsi" });
expect(img.getAttribute("src")).toMatch(/^data:image\/svg\+xml;base64,/);
});
test("shows 'Show' label when widget is not in container", () => {
(widgetApi.isAppInContainer as ReturnType<typeof vi.fn>).mockReturnValue(false);
render(<WidgetToggle app={app} roomId={roomId} widgetApi={widgetApi} i18nApi={i18nApi} />, { wrapper });
const button = screen.getByRole("button", { name: "Show My Widget" });
expect(button).toBeDefined();
});
test("shows 'Hide' label when widget is in container", () => {
(widgetApi.isAppInContainer as ReturnType<typeof vi.fn>).mockReturnValue(true);
render(<WidgetToggle app={app} roomId={roomId} widgetApi={widgetApi} i18nApi={i18nApi} />, { wrapper });
const button = screen.getByRole("button", { name: "Hide My Widget" });
expect(button).toBeDefined();
});
test("calls moveAppToContainer with 'top' when widget is not in container and button is clicked", async () => {
const user = userEvent.setup();
(widgetApi.isAppInContainer as ReturnType<typeof vi.fn>).mockReturnValue(false);
render(<WidgetToggle app={app} roomId={roomId} widgetApi={widgetApi} i18nApi={i18nApi} />, { wrapper });
const button = screen.getByRole("button", { name: "Show My Widget" });
await user.click(button);
expect(widgetApi.moveAppToContainer).toHaveBeenCalledWith(app, "top", roomId);
});
test("calls moveAppToContainer with 'right' when widget is in container and button is clicked", async () => {
const user = userEvent.setup();
(widgetApi.isAppInContainer as ReturnType<typeof vi.fn>).mockReturnValue(true);
render(<WidgetToggle app={app} roomId={roomId} widgetApi={widgetApi} i18nApi={i18nApi} />, { wrapper });
const button = screen.getByRole("button", { name: "Hide My Widget" });
await user.click(button);
expect(widgetApi.moveAppToContainer).toHaveBeenCalledWith(app, "right", roomId);
});
});
@@ -0,0 +1,7 @@
{
"extends": "../../../../tsconfig.json",
"compilerOptions": {
"jsx": "react-jsx"
},
"include": ["."]
}
@@ -7,12 +7,13 @@ Please see LICENSE files in the repository root for full details.
import { defineConfig } from "vitest/config";
import { env } from "node:process";
import { playwright } from "@vitest/browser-playwright";
const isGHA = env["GITHUB_ACTIONS"] !== undefined;
export default defineConfig({
test: {
include: ["tests/**/*.test.ts"],
include: ["tests/**/*.test.{ts,tsx}"],
exclude: ["./e2e/**/*", "./node_modules/**/*"],
reporters: isGHA
? ["default", ["vitest-sonar-reporter", { outputFile: "coverage/sonar-report.xml" }]]
@@ -22,5 +23,12 @@ export default defineConfig({
include: ["src/**/*.ts"],
reporter: [["lcov", { projectRoot: "../../../" }], "text"],
},
browser: {
enabled: true,
headless: true,
provider: playwright({}),
instances: [{ browser: "chromium" }],
},
setupFiles: ["tests/setupTests.ts"],
},
});