Skip to the code
cf.

Counter with limits

Build App: a counter that starts at 0 and shows Count: 0. A − button (labelled Decrease) takes the step away, and a + button (labelled Increase) adds it.

The step comes from a number box labelled Step, which starts at 1. If the box is empty, or holds anything but a whole number of at least 1, the buttons use a step of 1.

The count never goes below 0 or above 10: a step that would pass a limit stops at it. − is disabled at 0, and + is disabled at 10.

<p>Count: 3</p>
<button aria-label="Decrease">−</button>
<button aria-label="Increase">+</button>
<label>Step <input type="number" min="1" value="1" /></label>

What the tests check

  1. starts at 0, with − disabled and + enabled
    render(<App />);
    expect(screen.getByText("Count: 0")).toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Decrease" })).toBeDisabled();
    expect(screen.getByRole("button", { name: "Increase" })).toBeEnabled();
    
  2. the step box starts at 1
    render(<App />);
    expect(screen.getByLabelText("Step")).toHaveValue(1);
    
  3. + adds 1 and − takes 1 away
    render(<App />);
    const plus = screen.getByRole("button", { name: "Increase" });
    await user.click(plus);
    await user.click(plus);
    await user.click(plus);
    expect(screen.getByText("Count: 3")).toBeInTheDocument();
    await user.click(screen.getByRole("button", { name: "Decrease" }));
    expect(screen.getByText("Count: 2")).toBeInTheDocument();
    
  4. − turns on once the count is above 0
    render(<App />);
    await user.click(screen.getByRole("button", { name: "Increase" }));
    expect(screen.getByRole("button", { name: "Decrease" })).toBeEnabled();
    
  5. stops at 10, where + turns off
    render(<App />);
    const plus = screen.getByRole("button", { name: "Increase" });
    for (let i = 0; i < 10; i++) await user.click(plus);
    expect(screen.getByText("Count: 10")).toBeInTheDocument();
    expect(plus).toBeDisabled();
    await user.click(plus);
    expect(screen.getByText("Count: 10")).toBeInTheDocument();
    
  6. the step box sets how far each click moves
    render(<App />);
    const step = screen.getByLabelText("Step");
    await user.clear(step);
    await user.type(step, "3");
    const plus = screen.getByRole("button", { name: "Increase" });
    await user.click(plus);
    await user.click(plus);
    expect(screen.getByText("Count: 6")).toBeInTheDocument();
    
  7. a step that would pass 10 stops at 10
    render(<App />);
    const step = screen.getByLabelText("Step");
    await user.clear(step);
    await user.type(step, "4");
    const plus = screen.getByRole("button", { name: "Increase" });
    await user.click(plus);
    await user.click(plus);
    expect(screen.getByText("Count: 8")).toBeInTheDocument();
    await user.click(plus);
    expect(screen.getByText("Count: 10")).toBeInTheDocument();
    expect(plus).toBeDisabled();
    
  8. a step that would pass 0 stops at 0
    render(<App />);
    await user.click(screen.getByRole("button", { name: "Increase" }));
    await user.click(screen.getByRole("button", { name: "Increase" }));
    const step = screen.getByLabelText("Step");
    await user.clear(step);
    await user.type(step, "5");
    await user.click(screen.getByRole("button", { name: "Decrease" }));
    expect(screen.getByText("Count: 0")).toBeInTheDocument();
    expect(screen.getByRole("button", { name: "Decrease" })).toBeDisabled();
    
  9. an empty step box counts as 1
    render(<App />);
    await user.clear(screen.getByLabelText("Step"));
    await user.click(screen.getByRole("button", { name: "Increase" }));
    expect(screen.getByText("Count: 1")).toBeInTheDocument();
    

Learn more

App.jsxSaved in this browser

Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.

Ctrl + Enter runs the tests (⌘ + Enter on a Mac).