Skip to content
Merged
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
1 change: 1 addition & 0 deletions packages/components/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default tseslint.config(
"src/components/Autocomplete/**",
"src/components/Cell/**",
"src/components/Chip/**",
"src/components/Dialog/**",
"src/components/Environment/**",
"src/components/Menu/**",
"src/components/OfflineBanner/**",
Expand Down
2 changes: 1 addition & 1 deletion packages/components/jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const testPathIgnorePatterns = [
"<rootDir>/src/components/Autocomplete/",
"<rootDir>/src/components/Cell/",
"<rootDir>/src/components/Chip/",
"<rootDir>/src/components/Dialog/",
"<rootDir>/src/components/Environment/",
"<rootDir>/src/components/Menu/",
"<rootDir>/src/components/OfflineBanner/",
Expand All @@ -42,7 +43,6 @@ const testPathIgnorePatterns = [
// checklist — both are slated for removal/replacement (Paper -> Card,
// PressableHighlight -> Pressable) once the library migration completes,
// so we're not adding new tests for code we're about to delete.
"<rootDir>/src/components/SelectionControls/",
];

// Guard against the exact footgun these lists create: a follow-up PR adds
Expand Down
101 changes: 101 additions & 0 deletions packages/components/src/components/SelectionControls/Checkbox.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from "react";
import { fireEvent, render, screen } from "test-utils";
import { Checkbox } from "./index";

describe("Checkbox", () => {
it("renders the label", () => {
render(<Checkbox onPress={jest.fn()} label="Accept terms" />);
expect(screen.getByText("Accept terms")).toBeTruthy();
});

it("calls onPress with the opposite of checked when pressed", () => {
const onPressWhenUnchecked = jest.fn();
const { getByRole: getByRoleUnchecked } = render(
<Checkbox checked={false} onPress={onPressWhenUnchecked} />
);
fireEvent.press(getByRoleUnchecked("checkbox"));
expect(onPressWhenUnchecked).toHaveBeenCalledWith(true);

const onPressWhenChecked = jest.fn();
const { getByRole: getByRoleChecked } = render(
<Checkbox checked onPress={onPressWhenChecked} />
);
fireEvent.press(getByRoleChecked("checkbox"));
expect(onPressWhenChecked).toHaveBeenCalledWith(false);
});

it("reflects a different checked state via accessibilityState and icon for a different checked value", () => {
const {
getByRole: getUnchecked,
UNSAFE_getByProps: getPropsUnchecked,
} = render(<Checkbox checked={false} onPress={jest.fn()} />);
const { getByRole: getChecked, UNSAFE_getByProps: getPropsChecked } =
render(<Checkbox checked onPress={jest.fn()} />);

expect(getUnchecked("checkbox")).toHaveProp("accessibilityState", {
checked: false,
disabled: false,
});
expect(getChecked("checkbox")).toHaveProp("accessibilityState", {
checked: true,
disabled: false,
});

expect(
getPropsUnchecked({ name: "checkbox-blank-outline" })
).toBeTruthy();
expect(getPropsChecked({ name: "checkbox-marked" })).toBeTruthy();
});

it("shows an indeterminate state that takes precedence over checked", () => {
const { getByRole, UNSAFE_getByProps } = render(
<Checkbox checked indeterminate onPress={jest.fn()} />
);
expect(getByRole("checkbox")).toHaveProp("accessibilityState", {
checked: "mixed",
disabled: false,
});
expect(UNSAFE_getByProps({ name: "minus-box" })).toBeTruthy();
});

it("does not call onPress when disabled", () => {
const onPress = jest.fn();
render(<Checkbox onPress={onPress} disabled />);
fireEvent.press(screen.getByRole("checkbox"));
expect(onPress).not.toHaveBeenCalled();
});

it("exposes disabled state to assistive technology", () => {
render(<Checkbox onPress={jest.fn()} disabled />);
expect(screen.getByRole("checkbox")).toBeDisabled();
});

it("is non-interactive and reports disabled to assistive technology when onPress is not provided", () => {
render(<Checkbox label="Accept terms" />);
const checkbox = screen.getByRole("checkbox");
expect(() => fireEvent.press(checkbox)).not.toThrow();
expect(checkbox).toBeDisabled();
});

it("defaults accessibilityLabel to the label", () => {
render(<Checkbox onPress={jest.fn()} label="Accept terms" />);
expect(screen.getByRole("checkbox")).toHaveProp(
"accessibilityLabel",
"Accept terms"
);
});

it("lets an explicit accessibilityLabel override the label", () => {
render(
<Checkbox
onPress={jest.fn()}
label="Accept terms"
accessibilityLabel="Custom label"
/>
);
expect(screen.getByRole("checkbox")).toHaveProp(
"accessibilityLabel",
"Custom label"
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import React from "react";
import { fireEvent, render, screen } from "test-utils";
import { Radio } from "./index";

describe("Radio", () => {
it("renders the label", () => {
render(<Radio onPress={jest.fn()} label="Option A" />);
expect(screen.getByText("Option A")).toBeTruthy();
});

it("calls onPress with the opposite of checked when pressed", () => {
const onPressWhenUnchecked = jest.fn();
const { getByRole: getByRoleUnchecked } = render(
<Radio checked={false} onPress={onPressWhenUnchecked} />
);
fireEvent.press(getByRoleUnchecked("radio"));
expect(onPressWhenUnchecked).toHaveBeenCalledWith(true);

const onPressWhenChecked = jest.fn();
const { getByRole: getByRoleChecked } = render(
<Radio checked onPress={onPressWhenChecked} />
);
fireEvent.press(getByRoleChecked("radio"));
expect(onPressWhenChecked).toHaveBeenCalledWith(false);
});

it("reflects a different checked state via accessibilityState and icon for a different checked value", () => {
const { getByRole: getUnchecked, UNSAFE_getByProps: getPropsUnchecked } =
render(<Radio checked={false} onPress={jest.fn()} />);
const { getByRole: getChecked, UNSAFE_getByProps: getPropsChecked } =
render(<Radio checked onPress={jest.fn()} />);

expect(getUnchecked("radio")).toHaveProp("accessibilityState", {
checked: false,
disabled: false,
});
expect(getChecked("radio")).toHaveProp("accessibilityState", {
checked: true,
disabled: false,
});

expect(getPropsUnchecked({ name: "radiobox-blank" })).toBeTruthy();
expect(getPropsChecked({ name: "radiobox-marked" })).toBeTruthy();
});

it("does not call onPress when disabled", () => {
const onPress = jest.fn();
render(<Radio onPress={onPress} disabled />);
fireEvent.press(screen.getByRole("radio"));
expect(onPress).not.toHaveBeenCalled();
});

it("exposes disabled state to assistive technology", () => {
render(<Radio onPress={jest.fn()} disabled />);
expect(screen.getByRole("radio")).toBeDisabled();
});

it("is non-interactive and reports disabled to assistive technology when onPress is not provided", () => {
render(<Radio label="Option A" />);
const radio = screen.getByRole("radio");
expect(() => fireEvent.press(radio)).not.toThrow();
expect(radio).toBeDisabled();
});

it("defaults accessibilityLabel to the label", () => {
render(<Radio onPress={jest.fn()} label="Option A" />);
expect(screen.getByRole("radio")).toHaveProp(
"accessibilityLabel",
"Option A"
);
});

it("lets an explicit accessibilityLabel override the label", () => {
render(
<Radio
onPress={jest.fn()}
label="Option A"
accessibilityLabel="Custom label"
/>
);
expect(screen.getByRole("radio")).toHaveProp(
"accessibilityLabel",
"Custom label"
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from "react";
import { fireEvent, render, screen } from "test-utils";
import { Switch } from "./index";

describe("Switch", () => {
it("renders the label", () => {
render(<Switch label="Airplane mode" />);
expect(screen.getByText("Airplane mode")).toBeTruthy();
});

it("calls onChange with the opposite of active when pressed", () => {
const onChangeWhenInactive = jest.fn();
const { getByRole: getByRoleInactive } = render(
<Switch active={false} onChange={onChangeWhenInactive} />
);
fireEvent.press(getByRoleInactive("switch"));
expect(onChangeWhenInactive).toHaveBeenCalledWith(true);

const onChangeWhenActive = jest.fn();
const { getByRole: getByRoleActive } = render(
<Switch active onChange={onChangeWhenActive} />
);
fireEvent.press(getByRoleActive("switch"));
expect(onChangeWhenActive).toHaveBeenCalledWith(false);
});

it("reflects a different checked state via accessibilityState for a different active value", () => {
const { getByRole: getInactive } = render(<Switch active={false} />);
const { getByRole: getActive } = render(<Switch active />);

expect(getInactive("switch")).toHaveProp("accessibilityState", {
checked: false,
disabled: false,
});
expect(getActive("switch")).toHaveProp("accessibilityState", {
checked: true,
disabled: false,
});
});

it("does not call onChange when disabled", () => {
const onChange = jest.fn();
render(<Switch onChange={onChange} disabled />);
fireEvent.press(screen.getByRole("switch"));
expect(onChange).not.toHaveBeenCalled();
});

it("exposes disabled state to assistive technology", () => {
render(<Switch disabled />);
expect(screen.getByRole("switch")).toBeDisabled();
});
});
1 change: 1 addition & 0 deletions packages/components/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"src/components/Autocomplete",
"src/components/Cell",
"src/components/Chip",
"src/components/Dialog",
"src/components/Environment",
"src/components/Menu",
"src/components/OfflineBanner",
Expand Down
Loading