monday-ui-react-core
Version:
Official monday.com UI resources for application development in React.js
362 lines (319 loc) • 12 kB
JavaScript
import React from "react";
import { act, cleanup, fireEvent, render, screen } from "@testing-library/react";
import TextField from "../TextField";
import { TextFieldAriaLabel } from "../TextFieldConstants";
describe("TextField Tests", () => {
let inputComponent;
let onChangeStub;
const defaultPlaceHolder = "Place Holder Text";
let ref;
beforeEach(() => {
cleanup();
ref = { current: null };
onChangeStub = jest.fn();
jest.useFakeTimers("modern");
act(() => {
inputComponent = render(
<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" ref={ref} />
);
});
});
afterEach(() => {
jest.useRealTimers();
cleanup();
});
it("should display placeholder", () => {
expect(screen.getByPlaceholderText(defaultPlaceHolder)).toBeTruthy();
});
it("on input mutate should call the callback stub with the value", () => {
const input = screen.getByPlaceholderText(defaultPlaceHolder);
const value = "Value of input";
act(() => {
fireEvent.change(input, { target: { value } });
});
expect(input.value).toBe(value);
});
it("on input mutate should call the stub with the value", () => {
const input = screen.getByPlaceholderText(defaultPlaceHolder);
const value = "Value of input";
act(() => {
fireEvent.change(input, { target: { value } });
});
expect(onChangeStub.mock.calls[0][0]).toEqual(value);
});
it("should not change the value instantly when debounce value is provided", () => {
const { rerender } = inputComponent;
act(() => {
inputComponent = rerender(
<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" debounceRate={200} />
);
});
const value = "Value of input";
const input = screen.getByPlaceholderText(defaultPlaceHolder);
act(() => {
fireEvent.change(input, { target: { value } });
});
expect(onChangeStub.mock.calls.length).toEqual(0);
});
it("should be able to forward ref", () => {
const { rerender } = inputComponent;
act(() => {
inputComponent = rerender(
<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" debounceRate={200} ref={ref} />
);
});
expect(ref.current.classList.contains("input")).toBeTruthy();
});
it("should call the debounced function after time passed (fake timers)", () => {
const { rerender } = inputComponent;
const debounceTime = 200;
act(() => {
inputComponent = rerender(
<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" debounceRate={debounceTime} />
);
});
const value = "Value of input";
const input = screen.getByPlaceholderText(defaultPlaceHolder);
act(() => {
fireEvent.change(input, { target: { value } });
});
jest.advanceTimersByTime(debounceTime + 1);
expect(onChangeStub.mock.calls.length).toEqual(1);
});
it("should be disabled", () => {
const { rerender } = inputComponent;
act(() => {
inputComponent = rerender(
<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" disabled />
);
});
const input = screen.getByPlaceholderText(defaultPlaceHolder);
expect(input).toBeDisabled;
});
it("title should be available", () => {
const { rerender, getByText } = inputComponent;
const title = "Title";
act(() => {
rerender(<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" disabled title={title} />);
});
const titleElement = getByText(title);
expect(titleElement).toBeTruthy();
});
it("title should not be available", () => {
const { rerender, queryByText } = inputComponent;
const title = "My Awesome Heading";
act(() => {
rerender(<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" disabled />);
});
const titleElement = queryByText(title);
expect(titleElement).toBeFalsy();
});
it("should display an icon", () => {
const iconNames = { primary: "Primary Icon" };
const { rerender, queryByLabelText } = inputComponent;
act(() => {
rerender(
<TextField
placeholder={defaultPlaceHolder}
onChange={onChangeStub}
id="test"
iconName="test"
iconsNames={iconNames}
/>
);
});
const icon = queryByLabelText(iconNames.primary);
expect(icon).toBeTruthy();
});
it("should not display icon", () => {
const { rerender, queryByLabelText } = inputComponent;
act(() => {
rerender(<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" />);
});
const icon = queryByLabelText("test-icon");
expect(icon).toBeFalsy();
});
describe("char count and limit", () => {
it("should display char count on initial", () => {
const { rerender, queryByLabelText } = inputComponent;
const value = "hello";
act(() => {
rerender(
<TextField
placeholder={defaultPlaceHolder}
onChange={onChangeStub}
id="char-count-test"
showCharCount
value={value}
/>
);
});
const charCount = queryByLabelText(TextFieldAriaLabel.CHAR);
expect(parseInt(charCount.innerHTML, 10)).toBe(value.length);
});
it("should display char count and max length on initial", () => {
const { rerender, getByText } = inputComponent;
const value = "hello";
act(() => {
rerender(
<TextField
placeholder={defaultPlaceHolder}
onChange={onChangeStub}
id="char-count-test"
showCharCount
value={value}
maxLength={10}
/>
);
});
expect(getByText(`${value.length}/10`)).toBeTruthy();
});
it("char count should display correctly after changing value", () => {
const { rerender, queryByLabelText } = inputComponent;
let value = "hello";
act(() => {
rerender(
<TextField
placeholder={defaultPlaceHolder}
onChange={onChangeStub}
id="char-count-test"
showCharCount
value={value}
/>
);
});
const input = screen.getByPlaceholderText(defaultPlaceHolder);
value = "Value of input";
act(() => {
fireEvent.change(input, { target: { value } });
});
const charCount = queryByLabelText(TextFieldAriaLabel.CHAR);
expect(parseInt(charCount.innerHTML, 10)).toEqual(value.length);
});
it("should prevent typing when character limit is reached and allowExceedingMaxLength is false", () => {
const { rerender } = inputComponent;
act(() => {
rerender(
<TextField placeholder={defaultPlaceHolder} showCharCount maxLength={5} allowExceedingMaxLength={false} />
);
});
const input = screen.getByPlaceholderText(defaultPlaceHolder);
// This correctly tests the maxLength attribute be properly set on the input element
// Using fireEvent bypasses the maxLength where a user wouldn't:
// https://github.com/testing-library/user-event/issues/591#issuecomment-517816296
expect(input).toHaveAttribute("maxlength", "5");
});
it("should allow typing beyond character limit when allowExceedingMaxLength is true", () => {
const { rerender, getByText } = inputComponent;
act(() => {
rerender(
<TextField placeholder={defaultPlaceHolder} showCharCount maxLength={5} allowExceedingMaxLength={true} />
);
});
const input = screen.getByPlaceholderText(defaultPlaceHolder);
act(() => {
fireEvent.change(input, { target: { value: "123456" } });
});
expect(input).toHaveValue("123456");
expect(input).not.toHaveAttribute("maxlength");
expect(getByText("6/5")).toBeTruthy();
});
});
describe("validation text", () => {
it("should not show validation", () => {
const { queryByLabelText } = inputComponent;
const validationTextNode = queryByLabelText(TextFieldAriaLabel.VALIDATION_TEXT);
expect(validationTextNode).toBeNull();
});
});
describe("types", () => {
it("default type should be text", () => {
const input = screen.getByPlaceholderText(defaultPlaceHolder);
expect(input.type).toBe(TextField.types.TEXT);
});
it("type should be password", () => {
const { rerender } = inputComponent;
act(() => {
inputComponent = rerender(
<TextField
placeholder={defaultPlaceHolder}
onChange={onChangeStub}
id="test"
type={TextField.types.PASSWORD}
/>
);
});
const input = screen.getByPlaceholderText(defaultPlaceHolder);
expect(input.type).toBe(TextField.types.PASSWORD);
});
it("type should be tel", () => {
const { rerender } = inputComponent;
act(() => {
inputComponent = rerender(
<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" type={TextField.types.TEL} />
);
});
const input = screen.getByPlaceholderText(defaultPlaceHolder);
expect(input.type).toBe(TextField.types.TEL);
});
it("type should be url", () => {
const { rerender } = inputComponent;
act(() => {
inputComponent = rerender(
<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" type={TextField.types.URL} />
);
});
const input = screen.getByPlaceholderText(defaultPlaceHolder);
expect(input.type).toBe(TextField.types.URL);
});
it("type should be email", () => {
const { rerender } = inputComponent;
act(() => {
inputComponent = rerender(
<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" type={TextField.types.EMAIL} />
);
});
const input = screen.getByPlaceholderText(defaultPlaceHolder);
expect(input.type).toBe(TextField.types.EMAIL);
});
});
describe("autocomplete", () => {
it("should add autocomplete attr and set it to on", () => {
const { container } = render(
<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" autoComplete="on" />
);
const element = container.querySelector('[autocomplete="on"]');
expect(element).toBeTruthy();
});
it("should add autocomplete attr and set it to off", () => {
const { container } = render(
<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" autoComplete="off" />
);
const element = container.querySelector('[autocomplete="off"]');
expect(element).toBeTruthy();
});
});
it("should trim the value if trim is true", () => {
const { rerender } = inputComponent;
act(() => {
inputComponent = rerender(<TextField placeholder={defaultPlaceHolder} onChange={onChangeStub} id="test" trim />);
});
const input = screen.getByPlaceholderText(defaultPlaceHolder);
const value = "Value of input ";
act(() => {
fireEvent.change(input, { target: { value } });
});
expect(input.value).toBe(value.trim());
});
describe("controlled", () => {
it("should call onChange with the new value when controlled is true", () => {
const handleChange = jest.fn();
render(<TextField placeholder="Enter text" onChange={handleChange} value="value" controlled />);
const input = screen.getByPlaceholderText("Enter text");
expect(input.value).toBe("value");
fireEvent.change(input, { target: { value: "new value" } });
expect(handleChange).toHaveBeenCalledTimes(1);
expect(handleChange).toHaveBeenCalledWith("new value", expect.anything());
});
});
});