Skip to the code
cf.

Traffic light

The road outside the tea shop is getting a new crossing, and the council wants a model of its traffic light. Build TrafficLight: a light that starts on red and cycles red, then green, then yellow, then red again, for as long as it's on the page.

Red stays on for redMs milliseconds, green for greenMs, and yellow for yellowMs. They're props, with the defaults 4000, 3000, and 1000.

Show the light that's on as one word in a <p>: Red, Green, or Yellow. Draw the three lamps however you like, as long as they have no text.

Use one timer at a time. When the traffic light leaves the page, the cycle stops and no timer is left running.

<TrafficLight />                                    // red 4 s, green 3 s, yellow 1 s
<TrafficLight redMs={2000} greenMs={2000} yellowMs={500} />
 
// renders, while it's green:
<p>Green</p>

What the tests check

  1. starts on red
    useFakeTimers();
    render(<App />);
    expect(screen.getByText("Red")).toBeInTheDocument();
    expect(screen.queryByText("Green")).not.toBeInTheDocument();
    expect(screen.queryByText("Yellow")).not.toBeInTheDocument();
    
  2. turns green after 4 seconds
    const clock = useFakeTimers();
    render(<App />);
    await clock.advance(3999);
    expect(screen.getByText("Red")).toBeInTheDocument();
    await clock.advance(1);
    expect(screen.getByText("Green")).toBeInTheDocument();
    expect(screen.queryByText("Red")).not.toBeInTheDocument();
    
  3. turns yellow 3 seconds after green
    const clock = useFakeTimers();
    render(<App />);
    await clock.advance(4000);
    await clock.advance(2999);
    expect(screen.getByText("Green")).toBeInTheDocument();
    await clock.advance(1);
    expect(screen.getByText("Yellow")).toBeInTheDocument();
    
  4. goes back to red 1 second after yellow
    const clock = useFakeTimers();
    render(<App />);
    await clock.advance(7000);
    expect(screen.getByText("Yellow")).toBeInTheDocument();
    await clock.advance(1000);
    expect(screen.getByText("Red")).toBeInTheDocument();
    
  5. keeps cycling
    const clock = useFakeTimers();
    render(<App />);
    await clock.advance(8000 * 3);
    expect(screen.getByText("Red")).toBeInTheDocument();
    await clock.advance(4000);
    expect(screen.getByText("Green")).toBeInTheDocument();
    await clock.advance(3000);
    expect(screen.getByText("Yellow")).toBeInTheDocument();
    
  6. takes its times from props
    const clock = useFakeTimers();
    render(<App redMs={1000} greenMs={500} yellowMs={200} />);
    await clock.advance(1000);
    expect(screen.getByText("Green")).toBeInTheDocument();
    await clock.advance(500);
    expect(screen.getByText("Yellow")).toBeInTheDocument();
    await clock.advance(200);
    expect(screen.getByText("Red")).toBeInTheDocument();
    
  7. uses one timer at a time
    const clock = useFakeTimers();
    render(<App />);
    expect(clock.pending()).toBe(1);
    await clock.advance(4000);
    expect(clock.pending()).toBe(1);
    await clock.advance(4000);
    expect(clock.pending()).toBe(1);
    
  8. stops when it leaves the page
    const clock = useFakeTimers();
    const { unmount } = render(<App />);
    await clock.advance(5000);
    unmount();
    expect(clock.pending()).toBe(0);
    

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).