Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internals/test-helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { act, render } from "@testing-library/react";
export { act, cleanup, fireEvent, render } from "@testing-library/react";
export { default as userEvent } from "@testing-library/user-event";
export * from "shadow-dom-testing-library";
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
"@types/stream-json": "^1.7.7",
"@vitejs/plugin-react": "^4.3.3",
"custom-elements-manifest": "^2.1.0",
"element-internals-polyfill": "^1.3.13",
"eslint": "^9.12.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.3",
Expand Down
11 changes: 11 additions & 0 deletions packages/web-components/jest.setup.ts
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
import { cleanup } from "@internals/test-helpers";
import { afterEach, beforeAll, jest } from "@jest/globals";
import "@testing-library/jest-dom/jest-globals";

beforeAll(async () => {
await import("element-internals-polyfill");
});

afterEach(() => {
cleanup();
jest.clearAllMocks();
});
6 changes: 5 additions & 1 deletion packages/web-components/src/button/base/base-button.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,10 @@ export abstract class BaseButton extends BaseClass implements FormSubmitter {
*/
protected renderSpinner() {
return html`
<div class="icon spinner">
<div
class="icon spinner"
data-testid="tapsi-button-spinner"
>
<tapsi-spinner></tapsi-spinner>
</div>
`;
Expand Down Expand Up @@ -158,6 +161,7 @@ export abstract class BaseButton extends BaseClass implements FormSubmitter {
return html`
<button
id="root"
data-testid="tapsi-button-root"
part="root"
class=${rootClasses}
tabindex=${this.tabIndex}
Expand Down
108 changes: 108 additions & 0 deletions packages/web-components/src/button/icon-button/icon-button.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { TapsiIconButton } from "./index.ts";

import { render, screen, userEvent } from "@internals/test-helpers";
import { describe, expect, it, jest } from "@jest/globals";
import { createComponent } from "@lit/react";
import React from "react";
import {
itShouldMount,
itSupportsClassName,
itSupportsDataSetProps,
itSupportsStyle,
} from "../../internals/tests.tsx";

const IconButton = createComponent({
react: React,
elementClass: TapsiIconButton,
tagName: "tapsi-icon-button",
});

const handleClick = jest.fn();
const getTestIconButton = () => screen.getByTestId("test-icon-button");

const mockRequiredProps = {
"data-testid": "test-icon-button",
label: "test-icon-button-label",
};

describe("🧪 button/icon-button: UI", () => {
itShouldMount(IconButton, mockRequiredProps);
itSupportsClassName(IconButton, mockRequiredProps);
itSupportsStyle(IconButton, mockRequiredProps);
itSupportsDataSetProps(IconButton, mockRequiredProps);

it("should trigger `handleClick` function after clicking on the icon button", async () => {
render(
<IconButton
{...mockRequiredProps}
onClick={handleClick}
/>,
);
const testIconButton = getTestIconButton();

const { click } = userEvent.setup();

await click(testIconButton);

expect(handleClick).toHaveBeenCalledTimes(1);
});

it("should trigger `handleClick` function after focusing on the button and pressing Enter", async () => {
render(
<IconButton
{...mockRequiredProps}
onClick={handleClick}
/>,
);
const testIconButton = getTestIconButton();

const { tab, keyboard } = userEvent.setup();

await tab();
expect(testIconButton).toHaveFocus();
await keyboard("{Enter}");

expect(handleClick).toHaveBeenCalledTimes(1);
});

it("should not be able to trigger `handleClick` function while `disabled`", async () => {
render(
<IconButton
{...mockRequiredProps}
onClick={handleClick}
disabled
/>,
);
const testIconButton = getTestIconButton();

const { tab, click } = userEvent.setup();

await tab();
expect(testIconButton).not.toHaveFocus();

await click(testIconButton);

expect(handleClick).toHaveBeenCalledTimes(0);
});

it("should have `aria-label` when the host has a `label` property", async () => {
render(<IconButton {...mockRequiredProps} />);

expect(
await screen.findByShadowTestId("tapsi-button-root"),
).toHaveAttribute("aria-label", "test-icon-button-label");
});

it("should show spinner with `loading` prop", async () => {
render(
<IconButton
{...mockRequiredProps}
loading
/>,
);

expect(
await screen.findByShadowTestId("tapsi-button-spinner"),
).toBeInTheDocument();
});
});
175 changes: 175 additions & 0 deletions packages/web-components/src/button/standard/button.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { TapsiButton } from "./index.ts";

import { render, screen, userEvent } from "@internals/test-helpers";
import { describe, expect, it, jest } from "@jest/globals";
import { createComponent } from "@lit/react";
import React, { type FormEventHandler } from "react";
import {
itShouldMount,
itSupportsClassName,
itSupportsDataSetProps,
itSupportsStyle,
} from "../../internals/tests.tsx";

const Button = createComponent({
react: React,
elementClass: TapsiButton,
tagName: "tapsi-button",
});

const handleClick = jest.fn();
const getTestButton = () => screen.getByTestId("test-button");

const mockRequiredProps = {
"data-testid": "test-button",
label: "test-button-label",
};

describe("🧪 button/standard: UI", () => {
itShouldMount(Button, mockRequiredProps);
itSupportsClassName(Button, mockRequiredProps);
itSupportsStyle(Button, mockRequiredProps);
itSupportsDataSetProps(Button, mockRequiredProps);

it("should trigger `handleClick` function after clicking on the button", async () => {
render(
<Button
{...mockRequiredProps}
onClick={handleClick}
/>,
);
const testButton = getTestButton();

const { click } = userEvent.setup();

await click(testButton);

expect(handleClick).toHaveBeenCalledTimes(1);
});

it("should trigger `handleClick` function after focusing on the button and pressing Enter", async () => {
render(
<Button
{...mockRequiredProps}
onClick={handleClick}
/>,
);
const testButton = getTestButton();

const { tab, keyboard } = userEvent.setup();

await tab();
expect(testButton).toHaveFocus();
await keyboard("{Enter}");

expect(handleClick).toHaveBeenCalledTimes(1);
});

it("should not be able to trigger `handleClick` function while `disabled`", async () => {
render(
<Button
{...mockRequiredProps}
onClick={handleClick}
disabled
/>,
);
const testButton = getTestButton();

const { tab, click } = userEvent.setup();

await tab();
expect(testButton).not.toHaveFocus();

await click(testButton);

expect(handleClick).toHaveBeenCalledTimes(0);
});

it("should have `aria-label` when the host has a `label` property", async () => {
render(<Button {...mockRequiredProps} />);

expect(
await screen.findByShadowTestId("tapsi-button-root"),
).toHaveAttribute("aria-label", "test-button-label");
});

it("should show spinner with `loading` prop", async () => {
render(
<Button
{...mockRequiredProps}
loading
/>,
);

expect(
await screen.findByShadowTestId("tapsi-button-spinner"),
).toBeInTheDocument();
});

it("should work inside a form", async () => {
const handleSubmit = jest.fn();
const handleReset = jest.fn();

const submitHandler: FormEventHandler<HTMLFormElement> = event => {
event.preventDefault();
handleSubmit(event);
};

const resetHandler: FormEventHandler<HTMLFormElement> = event => {
event.preventDefault();
handleReset(event);
};

const getForm = () => screen.getByTestId<HTMLFormElement>("form");

const getSubmitButton = () =>
screen.getByTestId<TapsiButton>("submit-button");

const getResetButton = () =>
screen.getByTestId<TapsiButton>("reset-button");

const getCheckbox = () =>
screen.getByTestId<HTMLInputElement>("test-checkbox");

const { click } = userEvent.setup();

const getFormData = () => new FormData(getForm());

const getCheckboxValue = () => getFormData().get("n");

render(
<form
data-testid="form"
onSubmit={submitHandler}
onReset={resetHandler}
>
<input
data-testid="test-checkbox"
type="checkbox"
name="n"
/>
<Button
data-testid="submit-button"
type="submit"
>
submit
</Button>
<Button
data-testid="reset-button"
type="reset"
>
reset
</Button>
</form>,
);

await click(getCheckbox());
await click(getSubmitButton());

expect(handleSubmit.mock.calls.length).toBe(1);
expect(getCheckboxValue()).toBe("on");

await click(getResetButton());
expect(handleReset.mock.calls.length).toBe(1);
});
});
1 change: 1 addition & 0 deletions packages/web-components/src/internals/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./animations.ts";
export * from "./keyboard.ts";
export * from "./tests.tsx";
export * from "./tokens.ts";
Loading
Loading