Pomodoro Clock is working but test 14 and 16 fails

My Pomodoro clock is functioning properly however I cannot pass test 14 and 16 even though I’ve been working on these project for almost 3 weeks now.

  • Test 14: When I click the element with the id of “session-decrement”, the value within id=“session-length” decrements by a value of 1, and I can see the updated value.

  • Test 16: I should not be able to set a session or break length to <= 0.

Specifically, test 16 is what bothering me since it’s properly the one causing the test 14 to not pass as well as the FFC tests taking too long to complete. One thing I noticed about test 16 it is says less or equal to zero. But when you looked at other projects, they’re all doing the same approach like me that is they are not allowing the session/break length be less than 1 and yet their code is passing.

What I find strange about these tests is my incrementing and decrementing functions are identical. If I can pass the incrementing tests, then I should be able to pass the decrementing tests too.

Link to Codepen: https://codepen.io/mohammedasker/pen/gOgeWRo?editors=0010

Code:

function App() {
  const [sessionTimer, setSessionTimer] = React.useState(25);
  const [breakTimer, setBreakTimer] = React.useState(5);
  const [timerLeft, setTimerLeft] = React.useState(sessionTimer * 60);
  const [timerType, setTimerType] = React.useState("Session");
  const [pause, setPause] = React.useState(true);
  const starter = React.useRef(null);
  
  // Create function that will play the audio
  const playAudio = () => {
    const sound = document.getElementById("beep");
    sound.play();
  }
  
  // Create function that will stop the audio
  // const stopAudio = () => {
  //   const sound = document.getElementById("beep");
  //   sound.pause();
  // }
  
  // Create function that handles countdown
  // If the timer greater than 0, decrement the timer by 1
  const changeTime = () => {
    if (timerLeft > 0) {
      setTimerLeft((timerLeft) => timerLeft - 1)
    }
    // If the timer reaches 0, change the type of timer
    if (timerLeft == 0) {
      // If the type of timer is session, switch the type to break, set the timer to break, and play the audio
      if (timerType == "Session") {
        setTimerType("Break")
        setTimerLeft(breakTimer * 60)
        playAudio()
      }
      // If the type of timer is break, switch the type to session, set the timer to session, and play the audio
      else {
        setTimerType("Session")
        setTimerLeft(sessionTimer * 60)
        playAudio()
      }
    }
  }
  
  // If the timer is not paused, start the timer countdown
  React.useEffect(() => {
    if(!pause) {
      const interval = setInterval(changeTime, 1000)
      return () => clearInterval(interval)
    }
  })
  
  // Create function that will handle time starter
  const startTimer = () => {
    setPause(false)
  }
  
  // Create function that will handle time stopper
  const stopTimer = () => {
    setPause(true)
    clearInterval(starter.current)
  }
  
  // Create a function that will handle start and pause timer
  const startAndPause = () => {
    // If the timer is already paused, start the timer
    if (pause) {
      startTimer()
    } else {
    // If the timer is already started, pause the timer
      stopTimer()
    }
  }
  
  // Create a function that will reset the timer, timer type, and timer length to default and pause the timer
  const handleReset = () => {
    setSessionTimer(25);
    setBreakTimer(5);
    setTimerLeft(1500);
    setTimerType("Session");
    stopTimer();
  };
  
  // Create a function that will handle the decrement of session length
  const handleSessionDecrement = () => {
    if (sessionTimer === 1) {
      return;
    }
    setSessionTimer(sessionTimer - 1);
    if (timerType == "Session") {
      setTimerLeft((sessionTimer - 1) * 60)
    }
  };
  
  // Create a function that will handle the increment of session length
  const handleSessionIncrement = () => {
    if (sessionTimer >= 60) {
      return;
    }
    setSessionTimer(sessionTimer + 1);
    if (timerType == "Session") {
      setTimerLeft((sessionTimer + 1) * 60)
    }
  };
  
  // Create a function that will handle the decrement of break length
  const handleBreakDecrement = () => {
     if (breakTimer === 1) {
      return;
    }
    setBreakTimer(breakTimer - 1);
    if (timerType == "Break") {
      setBreakTimer((breakTimer - 1) * 60)
    }
  };
  
  // Create a function that will handle the increment of break length
  const handleBreakIncrement = () => {
    if (breakTimer >= 60) {
      return;
    }
    setBreakTimer(breakTimer + 1);
    if (timerType == "Break") {
      setBreakTimer((breakTimer + 1) * 60)
    }
  };
  
  // Create a function that will convert the timer into mm:ss format instead of seconds
  const formatTime = () => {
    let minutes = Math.floor(timerLeft / 60);
    let seconds = timerLeft - minutes * 60;
    
    if (minutes < 10) {
      minutes = "0" + minutes;
    }
    if (seconds < 10) {
      seconds = "0" + seconds;
    }

    return `${minutes}:${seconds}`;
  };
  
  return (
    <div className='bg-dark'>
    <div className="container">
   <h1 className="text-light">Pomodoro Clock</h1>
      <div id="break-label" className="text-light">Break Length</div>
      <div id="break-length" className="text-light">{breakTimer}</div>
      <button id="break-increment" className="btn btn-success btn-lg" onClick={handleBreakIncrement}><i className="fas fa-arrow-up"></i></button>
      <button id="break-decrement" className="btn btn-danger btn-lg" onClick={handleBreakDecrement}><i className="fas fa-arrow-down"></i></button>
      <div id="session-label" className="text-light">Session Length</div>
      <div id="session-length" className="text-light">{sessionTimer}</div>
      <button id="session-increment" className="btn btn-success btn-lg" onClick={handleSessionIncrement}><i className="fas fa-arrow-up"></i></button>
      <button id="session-decrement" className="btn btn-danger btn-lg"><i className="fas fa-arrow-down" onClick={handleSessionDecrement}></i></button>
      <div id="timer-label" className="text-light">{timerType}</div>
      <div id="time-left" className="text-light"><h1>{formatTime()} </h1></div>
      <button id="start_stop" className="btn btn-warning btn-lg" onClick={startAndPause}>{pause ? <i className="fas fa-play"></i> : <i className="fas fa-pause"></i>}</button>
      <button id="reset" className="btn btn-warning btn-lg" onClick={handleReset}><i className="fas fa-sync-alt"></i></button>
      <audio id="beep" src="https://raw.githubusercontent.com/freeCodeCamp/cdn/master/build/testable-projects-fcc/audio/BeepSound.wav"/>
      </div>
      </div>
  )
}

ReactDOM.render(
  <App/>,
  document.getElementById('root')
);

Hi, all! I’ve finished building 25 + 5 clock project and almost all features are working when tested manually. However, there are 4 tests that are still failing and they are:

  1. When I click the element with the id of “session-decrement”, the value within id=“session-length” decrements by a value of 1, and I can see the updated value. Error: expected ‘25’ to equal ‘21’

  2. I should not be able to set a session or break length to <= 0.
    Value in element with id of “session-length” is less than 1.: expected ‘25’ to equal ‘1’

  3. When a countdown reaches zero (NOTE: timer MUST reach 00:00), a sound indicating that time is up should play. This should utilize an HTML5 <audio> tag and have a corresponding id=“beep”. Error: Timer has reached zero but audio is not playing while it should.: expected true to be false

  4. The audio element with id of “beep” must stop playing and be rewound to the beginning when the element with the id of “reset” is clicked.
    Error: Audio element was not stopped when reset was clicked.: expected false to be true

I tried to fix these bugs for long time and read countless forum posts related to this challenge. Despite my best efforts, I wasn’t able to figure out what is causing this issues. Also, I tried to fix test 4 by creating a stopAudio() function but if I did this, it will cause other tests to fail and hence I comment out temporary.

Can you please guide me why this tests are failing? Thank you.

Link to codepen: https://codepen.io/mohammedasker/pen/gOgeWRo?editors=0010

Hi @Masker ,

Tried to decrease the session length after increasing the break length - it doesn’t work.

Hi @Masker ,

Not sure if you’ve already debugged this. I found a few things which could be causing the tests to fail.

For #1 and #2 tests, the issue is that you’ve placed onClick handler of session decrement inside the font awesome , i tag. Hence, the session decrement is triggered only if the button is clicked exactly at the center -on the arrow-down icon and doesn’t work if you click on the corners of the button.

For test #3, there seems to be a time lag although the time interval has been set to 1000. The below link could be useful.

Finally for test #4, setting the currentTime = 0 attribute for your audio ‘beep’ element inside the reset button handler should help.

Hope it’s not too late and that it helps :slightly_smiling_face:

1 Like

That was very helpful, @manjupillai16! I didn’t noticed that I placed the session decrement function on the wrong place. This explains why that particular button is kinda acting funny whenever I clicked it.

Only 2 audio tests are left now. I will keep you updated when I stuck again.

Updated: 28/29 passed! Only third test is left and this one is kind of complicated. I’ll use the link you provided as a reference and see if this will fix it.

1 Like

OK, I noticed when I test the timer manually…

So when the timer reaches 00:00, it supposed to play sound and change the type of timer. But what it actually happens is there is a one second delay before the sound is played.

For example: what I expect is when the session reaches 00:00, it will change to break length 01:00 and play the sound. Instead of playing the sound at 01:00 precise, it plays the sound when the timer is 00:59. This explains why the last test is not passing.

One possible thing I can do is to play the sound when the time hits zero or at least below one. Problem is when I try change the timerLeft == 0 to timerLeft < 1 or timerLeft <= 0 or simply making any kind of change to the timer, not only it will fail the test, but it will also broke other tests.

All I’m saying is getting the last test to pass is going to be tough.

New update: I managed to pass test 3. It can now play sound at exact 00:00.

However, the 4th test is now failing and I don’t know why this happens. The test claims that the sound is not stopping to play when reset is clicked but this is not true. It definitely is stopping the sound from playing when I click the button.

I wonder if it’s because of the way I structured the code that prevents the test suite from recognizing that the sound has indeed rewind. Or maybe the test suite has a preferred way of stopping the sound as the error message has point out the hints: use the currentTime property of the audio element to rewind.

Anyhow, I’ll try to use currentTime property and see if this will satisfy the test requirements. Here’s the updated code:

function App() {
  const [sessionTimer, setSessionTimer] = React.useState(25);
  const [breakTimer, setBreakTimer] = React.useState(5);
  const [timerLeft, setTimerLeft] = React.useState(sessionTimer * 60);
  const [timerType, setTimerType] = React.useState("Session");
  const [pause, setPause] = React.useState(true);
  const starter = React.useRef(null);
  
  // Create function that will play the audio
  const playAudio = () => {
    const sound = document.getElementById("beep");
    sound.play();
  }
  
  // Create function that will stop the audio
  const stopAudio = () => {
    const sound = document.getElementById("beep");
    sound.pause();
  }
  
  // Create function that handles countdown
  // If the timer is greater than 0, decrement the timer by 1
  const changeTime = () => {
    if (timerLeft > 0) {
      setTimerLeft((timerLeft) => timerLeft - 1)
    }
    // If the timer reaches 0, play the audio
    if (timerLeft == 1){
      playAudio();
    }
    // If the timer reaches 0, change the type of timer
    if (timerLeft == 0) {
      // If the type of timer is session, switch the timer type to break, set the timer left to break
      if (timerType == "Session") {
        setTimerType("Break");
        setTimerLeft(breakTimer * 60);
      }
      // If the type of timer is break, switch the timer type to session, set the timer left to session
      else {
        setTimerType("Session");
        setTimerLeft(sessionTimer * 60);
      }
    }
  }
  
  // If the timer is not paused, start the timer countdown
  React.useEffect(() => {
    if(!pause) {
      const interval = setInterval(changeTime, 1000);
      return () => clearInterval(interval);
    }
  })
  
  // Create function that will handle time starter
  const startTimer = () => {
    setPause(false);
  }
  
  // Create function that will handle time stopper
  const stopTimer = () => {
    setPause(true);
    clearInterval(starter.current);
  }
  
  // Create a function that will handle start and pause timer
  const startAndPause = () => {
    // If the timer is already paused, start the timer
    if (pause) {
      startTimer();
    } else {
    // If the timer is already started, pause the timer
      stopTimer();
    }
  }
  
  // Create a function that will reset the timer, timer type, and timer length to default and pause the timer
  const handleReset = () => {
    setSessionTimer(25);
    setBreakTimer(5);
    setTimerLeft(1500);
    setTimerType("Session");
    stopTimer();
    stopAudio();
  };
  
  // Create a function that will handle the decrement of session length
  const handleSessionDecrement = () => {
    if (sessionTimer == 1) {
      return;
    }
    setSessionTimer(sessionTimer - 1);
    if (timerType == "Session") {
      setTimerLeft((sessionTimer - 1) * 60)
    }
  };
  
  // Create a function that will handle the increment of session length
  const handleSessionIncrement = () => {
    if (sessionTimer >= 60) {
      return;
    }
    setSessionTimer(sessionTimer + 1);
    if (timerType == "Session") {
      setTimerLeft((sessionTimer + 1) * 60)
    }
  };
  
  // Create a function that will handle the decrement of break length
  const handleBreakDecrement = () => {
     if (breakTimer == 1) {
      return;
    }
    setBreakTimer(breakTimer - 1);
    if (timerType == "Break") {
      setBreakTimer((breakTimer - 1) * 60)
    }
  };
  
  // Create a function that will handle the increment of break length
  const handleBreakIncrement = () => {
    if (breakTimer >= 60) {
      return;
    }
    setBreakTimer(breakTimer + 1);
    if (timerType == "Break") {
      setBreakTimer((breakTimer + 1) * 60)
    }
  };
  
  // Create a function that will convert the timer into mm:ss format instead of seconds
  const formatTime = () => {
    let minutes = Math.floor(timerLeft / 60);
    let seconds = timerLeft - minutes * 60;
    
    if (minutes < 10) {
      minutes = "0" + minutes;
    }
    if (seconds < 10) {
      seconds = "0" + seconds;
    }

    return `${minutes}:${seconds}`;
  };
  
  return (
    <div className='bg-dark'>
    <div className="container">
   <h1 className="text-light">Pomodoro Clock</h1>
      <div id="break-label" className="text-light">Break Length</div>
      <div id="break-length" className="text-light">{breakTimer}</div>
      <button id="break-increment" className="btn btn-success btn-lg" onClick={handleBreakIncrement}><i className="fas fa-arrow-up"></i></button>
      <button id="break-decrement" className="btn btn-danger btn-lg" onClick={handleBreakDecrement}><i className="fas fa-arrow-down"></i></button>
      <div id="session-label" className="text-light">Session Length</div>
      <div id="session-length" className="text-light">{sessionTimer}</div>
      <button id="session-increment" className="btn btn-success btn-lg" onClick={handleSessionIncrement}><i className="fas fa-arrow-up"></i></button>
      <button id="session-decrement" className="btn btn-danger btn-lg" onClick={handleSessionDecrement}><i className="fas fa-arrow-down"></i></button>
      <div id="timer-label" className="text-light">{timerType}</div>
      <div id="time-left" className="text-light"><h1>{formatTime()} </h1></div>
      <button id="start_stop" className="btn btn-warning btn-lg" onClick={startAndPause}>{pause ? <i className="fas fa-play"></i> : <i className="fas fa-pause"></i>}</button>
      <button id="reset" className="btn btn-warning btn-lg" onClick={handleReset}><i className="fas fa-sync-alt"></i></button>
      <audio id="beep" src="https://raw.githubusercontent.com/freeCodeCamp/cdn/master/build/testable-projects-fcc/audio/BeepSound.wav"/>
      </div>
      </div>
  )
}

ReactDOM.render(
  <App/>,
  document.getElementById('root')
);

Good news - I passed all the tests!

As it turns out, I should’ve use the currentTime property to pass the final test as suggested by Manju Nair and FFC test. I thought I should only stop the sound from playing on click but nope! I should’ve stop the sound and rewind the sound length back to zero at the same time.

I still have one more test to pass from JavaScript Calculator and after that, I can claim the front end libraries certification.

Once again, @manjupillai16 - thank you very much!

Hi @Masker ,

Glad to hear that. Good luck with your Calculator :+1:

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.