From a45e62296b8355e635b4c965da2fb730730987e5 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sat, 27 Jan 2024 09:41:59 -0500 Subject: [PATCH 01/33] html structure added some basic structural elements --- src/App.tsx | 5 ++++- src/StopWatch.tsx | 8 +++++++- src/StopWatchButton.tsx | 4 +++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 95351af9..a6ba8c3b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,10 @@ import React from 'react' +import StopWatch from './StopWatch' export default function App() { return( -
+
+ +
) } \ No newline at end of file diff --git a/src/StopWatch.tsx b/src/StopWatch.tsx index d58edc78..d6c6e358 100644 --- a/src/StopWatch.tsx +++ b/src/StopWatch.tsx @@ -1,7 +1,13 @@ import React from 'react' +import StopWatchButton from './StopWatchButton' export default function StopWatch() { return( -
+
+ + + + +
) } \ No newline at end of file diff --git a/src/StopWatchButton.tsx b/src/StopWatchButton.tsx index dbd7c174..62f0e2f1 100644 --- a/src/StopWatchButton.tsx +++ b/src/StopWatchButton.tsx @@ -2,6 +2,8 @@ import React from 'react' export default function StopWatchButton() { return( -
+
+ +
) } \ No newline at end of file From 4dc573edfda581e19af20404d22bc379f08d8da1 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sat, 27 Jan 2024 11:10:15 -0500 Subject: [PATCH 02/33] refactor --- src/App.tsx | 12 ++++++++++-- src/StopWatch.tsx | 14 +++++--------- src/StopWatchButton.tsx | 10 +++++----- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a6ba8c3b..dfe4e526 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,18 @@ import React from 'react' import StopWatch from './StopWatch' +import StopWatchButton from './StopWatchButton' -export default function App() { +function App() { return(
+
+ + + +
) -} \ No newline at end of file +} + +export default App; \ No newline at end of file diff --git a/src/StopWatch.tsx b/src/StopWatch.tsx index d6c6e358..72640d05 100644 --- a/src/StopWatch.tsx +++ b/src/StopWatch.tsx @@ -1,13 +1,9 @@ import React from 'react' -import StopWatchButton from './StopWatchButton' -export default function StopWatch() { +const StopWatch = () => { return( -
- - - - -
+
time display
) -} \ No newline at end of file +} + +export default StopWatch; \ No newline at end of file diff --git a/src/StopWatchButton.tsx b/src/StopWatchButton.tsx index 62f0e2f1..5668efbf 100644 --- a/src/StopWatchButton.tsx +++ b/src/StopWatchButton.tsx @@ -1,9 +1,9 @@ import React from 'react' -export default function StopWatchButton() { +const StopWatchButton = () => { return( -
- -
+ ) -} \ No newline at end of file +} + +export default StopWatchButton; \ No newline at end of file From af338854f7ed8672d132ee7a983d891b90dc9ba3 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sat, 27 Jan 2024 11:26:41 -0500 Subject: [PATCH 03/33] added button labels --- src/App.tsx | 6 +++--- src/StopWatchButton.tsx | 8 ++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index dfe4e526..2ddde8b4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,9 +7,9 @@ function App() {
- - - + + +
) diff --git a/src/StopWatchButton.tsx b/src/StopWatchButton.tsx index 5668efbf..7e7b5e9e 100644 --- a/src/StopWatchButton.tsx +++ b/src/StopWatchButton.tsx @@ -1,8 +1,12 @@ import React from 'react' -const StopWatchButton = () => { +interface buttonProps { + label: string +} + +const StopWatchButton = ({label}:buttonProps) => { return( - + ) } From 79672c23dde4aa607901c4ff276972de983bc03a Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sat, 27 Jan 2024 13:29:00 -0500 Subject: [PATCH 04/33] basic button functionality added basic functions to the start and reset buttons and passed them as props to the button components --- src/App.tsx | 29 ++++++++++++++++++++++++----- src/StopWatch.tsx | 8 ++++++-- src/StopWatchButton.tsx | 7 ++++--- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 2ddde8b4..9c99b3e7 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,34 @@ -import React from 'react' +import React, { useState } from 'react' import StopWatch from './StopWatch' import StopWatchButton from './StopWatchButton' function App() { + const [time, setTime] = useState(0); + const [isTiming, setIsTiming] = useState(false); + + const startTime = () => { + setIsTiming(!isTiming); + setTime(time + 1); + console.log('started!', isTiming, time); + } + + const addLap = () => { + return + } + + const resetStopwatch = () => { + setIsTiming(!isTiming); + setTime(0); + console.log('reset!', isTiming, time); + } + return(
- +
- - - + startTime()}/> + addLap()}/> + resetStopwatch()}/>
) diff --git a/src/StopWatch.tsx b/src/StopWatch.tsx index 72640d05..5e953465 100644 --- a/src/StopWatch.tsx +++ b/src/StopWatch.tsx @@ -1,8 +1,12 @@ import React from 'react' -const StopWatch = () => { +interface stopWatchProps { + time: number; +} + +const StopWatch = ({time} : stopWatchProps) => { return( -
time display
+
{time}
) } diff --git a/src/StopWatchButton.tsx b/src/StopWatchButton.tsx index 7e7b5e9e..d586eee0 100644 --- a/src/StopWatchButton.tsx +++ b/src/StopWatchButton.tsx @@ -1,12 +1,13 @@ import React from 'react' interface buttonProps { - label: string + label: string; + handleButtonClick: () => void; } -const StopWatchButton = ({label}:buttonProps) => { +const StopWatchButton = ({label, handleButtonClick}:buttonProps) => { return( - + ) } From 71f846cddbfe6eb69633bd807977737ef486148a Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sat, 27 Jan 2024 13:38:10 -0500 Subject: [PATCH 05/33] style import added some basic temporary styling --- src/App.css | 14 ++++++++++++++ src/App.tsx | 1 + src/StopWatch.tsx | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/App.css diff --git a/src/App.css b/src/App.css new file mode 100644 index 00000000..217de1a4 --- /dev/null +++ b/src/App.css @@ -0,0 +1,14 @@ +@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); + +.timeDisplay { + font-family: "Press Start 2P", system-ui; + margin-bottom: 50px; + font-size: 70px; +} + +button { + padding: 20px; + margin-right: 30px; + width: 100px; + cursor: pointer; +} \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 9c99b3e7..5e077df3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,3 +1,4 @@ +import './App.css'; import React, { useState } from 'react' import StopWatch from './StopWatch' import StopWatchButton from './StopWatchButton' diff --git a/src/StopWatch.tsx b/src/StopWatch.tsx index 5e953465..3e90031e 100644 --- a/src/StopWatch.tsx +++ b/src/StopWatch.tsx @@ -6,7 +6,7 @@ interface stopWatchProps { const StopWatch = ({time} : stopWatchProps) => { return( -
{time}
+
{time}
) } From e3a100f84067113aac08396e0fa0a3c815732acb Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sat, 27 Jan 2024 13:48:33 -0500 Subject: [PATCH 06/33] added useEffect --- src/App.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 5e077df3..38b3fae9 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import './App.css'; -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' import StopWatch from './StopWatch' import StopWatchButton from './StopWatchButton' @@ -7,10 +7,17 @@ function App() { const [time, setTime] = useState(0); const [isTiming, setIsTiming] = useState(false); + useEffect(() => { + console.log(time) + }, [time]); + + useEffect(() => { + console.log(isTiming) + }, [isTiming]); + const startTime = () => { - setIsTiming(!isTiming); + isTiming === false ? setIsTiming(!isTiming) : null; setTime(time + 1); - console.log('started!', isTiming, time); } const addLap = () => { @@ -18,9 +25,8 @@ function App() { } const resetStopwatch = () => { - setIsTiming(!isTiming); + isTiming === true ? setIsTiming(!isTiming) : null; setTime(0); - console.log('reset!', isTiming, time); } return( From a77a75b818b43039b8ced7ddd6abf0fb675dd792 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 10:02:57 -0500 Subject: [PATCH 07/33] Update index.html --- public/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/index.html b/public/index.html index 5953d96e..20afa261 100755 --- a/public/index.html +++ b/public/index.html @@ -1,7 +1,7 @@ - Template Site + Jake's Really Cool Stopwatch
From ab63b17ec80876b45977b2c87b24745033384d4a Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 11:50:58 -0500 Subject: [PATCH 08/33] added stop button added a stop button and changed the start/stop button to just a start button --- src/App.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 38b3fae9..707cd5fd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,6 +20,10 @@ function App() { setTime(time + 1); } + const stopTime = () => { + isTiming ? setIsTiming(!isTiming) : null; + } + const addLap = () => { return } @@ -33,7 +37,8 @@ function App() {
- startTime()}/> + startTime()}/> + stopTime()}/> addLap()}/> resetStopwatch()}/>
From 258d0eeca80594b5de16d0bfc14679dabc0def89 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 15:02:00 -0500 Subject: [PATCH 09/33] Update App.tsx Switched to using useRef instead of useState for managing the time --- src/App.tsx | 43 +++++++++++++++++++------------------------ 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 707cd5fd..ff5b2e98 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,27 +1,21 @@ import './App.css'; -import React, { useState, useEffect } from 'react' +import React, { useState, useRef } from 'react' import StopWatch from './StopWatch' import StopWatchButton from './StopWatchButton' function App() { - const [time, setTime] = useState(0); - const [isTiming, setIsTiming] = useState(false); + const intervalRef = useRef(null); + const [timeElapsed, setTimeElapsed] = useState(0); - useEffect(() => { - console.log(time) - }, [time]); - - useEffect(() => { - console.log(isTiming) - }, [isTiming]); - - const startTime = () => { - isTiming === false ? setIsTiming(!isTiming) : null; - setTime(time + 1); + const startStopwatch = () => { + intervalRef.current + ? null + : intervalRef.current = setInterval(() => setTimeElapsed(t => t + 0.01), 10); } - const stopTime = () => { - isTiming ? setIsTiming(!isTiming) : null; + const stopStopwatch = () => { + clearInterval(intervalRef.current); + intervalRef.current = null; } const addLap = () => { @@ -29,18 +23,19 @@ function App() { } const resetStopwatch = () => { - isTiming === true ? setIsTiming(!isTiming) : null; - setTime(0); + clearInterval(intervalRef.current); + intervalRef.current = null; + setTimeElapsed(0); } - return( + return (
- +
- startTime()}/> - stopTime()}/> - addLap()}/> - resetStopwatch()}/> + startStopwatch()} /> + stopStopwatch()} /> + addLap()} /> + resetStopwatch()} />
) From ad1ed9e8fa9a1b1e612f54747677a1921062bb49 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 17:05:18 -0500 Subject: [PATCH 10/33] Stopwatch display formatting Added formatTime function to properly format the data displayed on the stopwatch and changed the StopWatch.tsx time prop's type to string --- src/App.tsx | 17 +++++++++++++++-- src/StopWatch.tsx | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index ff5b2e98..44265cf2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,7 +10,7 @@ function App() { const startStopwatch = () => { intervalRef.current ? null - : intervalRef.current = setInterval(() => setTimeElapsed(t => t + 0.01), 10); + : intervalRef.current = setInterval(() => setTimeElapsed(t => t + 10), 10); } const stopStopwatch = () => { @@ -28,9 +28,22 @@ function App() { setTimeElapsed(0); } + const formatTime = (timeInMilliseconds: number) => { + const minutes = Math.floor((timeInMilliseconds % 3600000) / 60000); + const seconds = Math.floor((timeInMilliseconds % 60000) / 1000); + const milliseconds = Math.floor((timeInMilliseconds % 1000) / 10); + + const doubleDigits = (input: number) => { + const output = input < 10 ? `0${input}` : input; + return output; + }; + + return `${doubleDigits(minutes)}:${doubleDigits(seconds)}.${doubleDigits(milliseconds)}`; + } + return (
- +
startStopwatch()} /> stopStopwatch()} /> diff --git a/src/StopWatch.tsx b/src/StopWatch.tsx index 3e90031e..a5a93534 100644 --- a/src/StopWatch.tsx +++ b/src/StopWatch.tsx @@ -1,7 +1,7 @@ import React from 'react' interface stopWatchProps { - time: number; + time: string } const StopWatch = ({time} : stopWatchProps) => { From 1142e33e8b1f3999d6e6e1388e97b1ba945d0647 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 17:06:44 -0500 Subject: [PATCH 11/33] consistent quotation marks changed some double-quotes to single-quotes --- src/App.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 44265cf2..88c4bc7f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -45,10 +45,10 @@ function App() {
- startStopwatch()} /> - stopStopwatch()} /> - addLap()} /> - resetStopwatch()} /> + startStopwatch()} /> + stopStopwatch()} /> + addLap()} /> + resetStopwatch()} />
) From 17bfb8220a2a0bdb134ca9c6a5dac933a8b245f3 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 17:47:12 -0500 Subject: [PATCH 12/33] styling and classnames --- src/App.css | 56 ++++++++++++++++++++++++++++++++++++++--- src/App.tsx | 4 +-- src/StopWatch.tsx | 2 +- src/StopWatchButton.tsx | 2 +- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/App.css b/src/App.css index 217de1a4..7e1a4b22 100644 --- a/src/App.css +++ b/src/App.css @@ -1,14 +1,62 @@ @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); -.timeDisplay { +html { + font-size: 62.5%; +} + +body { + margin: 0; + padding: 0; +} + +.app { + display: flex; + width: 100vw; + height: 100vh; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.digital { font-family: "Press Start 2P", system-ui; - margin-bottom: 50px; - font-size: 70px; +} + +.timeDisplay { + margin: 0 0 50px; + font-size: 6rem; + width: 500px; + text-align: center; +} + +.buttonsContainer { + width: 500px; + display: flex; + justify-content: space-between; } button { padding: 20px; - margin-right: 30px; width: 100px; cursor: pointer; + border: none; + border-radius: 2px; + text-transform: uppercase; +} + +button:hover { + background-color: red; +} + +@media (max-width: 500px) { + html { + font-size: 40%; + } + .buttonsContainer { + flex-wrap: wrap; + width: 250px; + } + button { + margin-bottom: 30px; + } } \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 88c4bc7f..e18b5013 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -42,9 +42,9 @@ function App() { } return ( -
+
-
+
startStopwatch()} /> stopStopwatch()} /> addLap()} /> diff --git a/src/StopWatch.tsx b/src/StopWatch.tsx index a5a93534..da91ef51 100644 --- a/src/StopWatch.tsx +++ b/src/StopWatch.tsx @@ -6,7 +6,7 @@ interface stopWatchProps { const StopWatch = ({time} : stopWatchProps) => { return( -
{time}
+

{time}

) } diff --git a/src/StopWatchButton.tsx b/src/StopWatchButton.tsx index d586eee0..7d238623 100644 --- a/src/StopWatchButton.tsx +++ b/src/StopWatchButton.tsx @@ -7,7 +7,7 @@ interface buttonProps { const StopWatchButton = ({label, handleButtonClick}:buttonProps) => { return( - + ) } From f76045efdb3256a6dcef7089a1505f4e34d7abf5 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 18:28:02 -0500 Subject: [PATCH 13/33] Update App.tsx improved formatting/indentation --- src/App.tsx | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e18b5013..8921debe 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -45,10 +45,22 @@ function App() {
- startStopwatch()} /> - stopStopwatch()} /> - addLap()} /> - resetStopwatch()} /> + startStopwatch()} + /> + stopStopwatch()} + /> + addLap()} + /> + resetStopwatch()} + />
) From aff527114045ff1d3e41868306f58be58ea6ca72 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 18:35:44 -0500 Subject: [PATCH 14/33] refactor moved formatTime() into StopWatch.tsx --- src/App.tsx | 15 +-------------- src/StopWatch.tsx | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 8921debe..91e1f61c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -28,22 +28,9 @@ function App() { setTimeElapsed(0); } - const formatTime = (timeInMilliseconds: number) => { - const minutes = Math.floor((timeInMilliseconds % 3600000) / 60000); - const seconds = Math.floor((timeInMilliseconds % 60000) / 1000); - const milliseconds = Math.floor((timeInMilliseconds % 1000) / 10); - - const doubleDigits = (input: number) => { - const output = input < 10 ? `0${input}` : input; - return output; - }; - - return `${doubleDigits(minutes)}:${doubleDigits(seconds)}.${doubleDigits(milliseconds)}`; - } - return (
- +
{ + const formatTime = (timeInMilliseconds: number) => { + const minutes = Math.floor((timeInMilliseconds % 3600000) / 60000); + const seconds = Math.floor((timeInMilliseconds % 60000) / 1000); + const milliseconds = Math.floor((timeInMilliseconds % 1000) / 10); + + const doubleDigits = (input: number) => { + const output = input < 10 ? `0${input}` : input; + return output; + }; + + return `${doubleDigits(minutes)}:${doubleDigits(seconds)}.${doubleDigits(milliseconds)}`; + } + return( -

{time}

+

{formatTime(time)}

) } From 9904786d0976e56a117c743e4bd07f6a7eec5323 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 19:00:55 -0500 Subject: [PATCH 15/33] Refactor Revised file structure --- src/App.tsx | 4 ++-- src/StopWatch.tsx | 26 ------------------------ src/components/StopWatch.tsx | 14 +++++++++++++ src/{ => components}/StopWatchButton.tsx | 0 src/utils/formatTime.tsx | 14 +++++++++++++ 5 files changed, 30 insertions(+), 28 deletions(-) delete mode 100644 src/StopWatch.tsx create mode 100644 src/components/StopWatch.tsx rename src/{ => components}/StopWatchButton.tsx (100%) create mode 100644 src/utils/formatTime.tsx diff --git a/src/App.tsx b/src/App.tsx index 91e1f61c..29f3fef2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import './App.css'; import React, { useState, useRef } from 'react' -import StopWatch from './StopWatch' -import StopWatchButton from './StopWatchButton' +import StopWatch from './components/StopWatch' +import StopWatchButton from './components/StopWatchButton' function App() { const intervalRef = useRef(null); diff --git a/src/StopWatch.tsx b/src/StopWatch.tsx deleted file mode 100644 index a3d71ee6..00000000 --- a/src/StopWatch.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react' - -interface stopWatchProps { - time: number -} - -const StopWatch = ({time} : stopWatchProps) => { - const formatTime = (timeInMilliseconds: number) => { - const minutes = Math.floor((timeInMilliseconds % 3600000) / 60000); - const seconds = Math.floor((timeInMilliseconds % 60000) / 1000); - const milliseconds = Math.floor((timeInMilliseconds % 1000) / 10); - - const doubleDigits = (input: number) => { - const output = input < 10 ? `0${input}` : input; - return output; - }; - - return `${doubleDigits(minutes)}:${doubleDigits(seconds)}.${doubleDigits(milliseconds)}`; - } - - return( -

{formatTime(time)}

- ) -} - -export default StopWatch; \ No newline at end of file diff --git a/src/components/StopWatch.tsx b/src/components/StopWatch.tsx new file mode 100644 index 00000000..a77b6d78 --- /dev/null +++ b/src/components/StopWatch.tsx @@ -0,0 +1,14 @@ +import React from 'react' +import formatTime from '../utils/formatTime' + +interface stopWatchProps { + time: number +} + +const StopWatch = ({time} : stopWatchProps) => { + return( +

{formatTime(time)}

+ ) +} + +export default StopWatch; \ No newline at end of file diff --git a/src/StopWatchButton.tsx b/src/components/StopWatchButton.tsx similarity index 100% rename from src/StopWatchButton.tsx rename to src/components/StopWatchButton.tsx diff --git a/src/utils/formatTime.tsx b/src/utils/formatTime.tsx new file mode 100644 index 00000000..8f0f1f92 --- /dev/null +++ b/src/utils/formatTime.tsx @@ -0,0 +1,14 @@ +const formatTime = (timeInMilliseconds: number): string => { + const minutes = Math.floor((timeInMilliseconds % 3600000) / 60000); + const seconds = Math.floor((timeInMilliseconds % 60000) / 1000); + const milliseconds = Math.floor((timeInMilliseconds % 1000) / 10); + + const doubleDigits = (input: number) => { + const output = input < 10 ? `0${input}` : input; + return output; + }; + + return `${doubleDigits(minutes)}:${doubleDigits(seconds)}.${doubleDigits(milliseconds)}`; +} + +export default formatTime; \ No newline at end of file From 3e119c13d095d9c9dcab3ca25c782580b8954445 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 19:04:04 -0500 Subject: [PATCH 16/33] Formatting Added some semicolons --- src/App.tsx | 6 +++--- src/components/StopWatch.tsx | 6 +++--- src/components/StopWatchButton.tsx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 29f3fef2..5fb6f88d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,7 @@ import './App.css'; -import React, { useState, useRef } from 'react' -import StopWatch from './components/StopWatch' -import StopWatchButton from './components/StopWatchButton' +import React, { useState, useRef } from 'react'; +import StopWatch from './components/StopWatch'; +import StopWatchButton from './components/StopWatchButton'; function App() { const intervalRef = useRef(null); diff --git a/src/components/StopWatch.tsx b/src/components/StopWatch.tsx index a77b6d78..04dd264a 100644 --- a/src/components/StopWatch.tsx +++ b/src/components/StopWatch.tsx @@ -1,8 +1,8 @@ -import React from 'react' -import formatTime from '../utils/formatTime' +import React from 'react'; +import formatTime from '../utils/formatTime'; interface stopWatchProps { - time: number + time: number; } const StopWatch = ({time} : stopWatchProps) => { diff --git a/src/components/StopWatchButton.tsx b/src/components/StopWatchButton.tsx index 7d238623..9c036606 100644 --- a/src/components/StopWatchButton.tsx +++ b/src/components/StopWatchButton.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React from 'react'; interface buttonProps { label: string; From 0d737fcbe41d8dcf5e839eaf4cbbb66bc17c0500 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:07:05 -0500 Subject: [PATCH 17/33] Record Lap Times partial lap time functionality --- src/App.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 5fb6f88d..029cb3f6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,10 +2,12 @@ import './App.css'; import React, { useState, useRef } from 'react'; import StopWatch from './components/StopWatch'; import StopWatchButton from './components/StopWatchButton'; +import formatTime from './utils/formatTime'; function App() { const intervalRef = useRef(null); const [timeElapsed, setTimeElapsed] = useState(0); + const [lapTimes, setLapTimes] = useState([]); const startStopwatch = () => { intervalRef.current @@ -18,14 +20,15 @@ function App() { intervalRef.current = null; } - const addLap = () => { - return + const recordLap = () => { + setLapTimes(prevLaps => [...prevLaps, timeElapsed]); } const resetStopwatch = () => { clearInterval(intervalRef.current); intervalRef.current = null; setTimeElapsed(0); + setLapTimes([]); } return ( @@ -42,13 +45,22 @@ function App() { /> addLap()} + handleButtonClick={() => recordLap()} /> resetStopwatch()} />
+
+
    + {lapTimes.map((lap, i) => { + return ( +
  1. Lap {i + 1}: {formatTime(lap)}
  2. + ) + })} +
+
) } From 61e457e058eb9423e74a28d2ccfff5220d6f6869 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:08:54 -0500 Subject: [PATCH 18/33] Lap times update --- src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 029cb3f6..d1ed325b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,7 +21,7 @@ function App() { } const recordLap = () => { - setLapTimes(prevLaps => [...prevLaps, timeElapsed]); + timeElapsed ? setLapTimes(prevLaps => [...prevLaps, timeElapsed]) : null; } const resetStopwatch = () => { From 30c6b419a59ce972b1a904c691364053d7086d96 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:39:26 -0500 Subject: [PATCH 19/33] Lap times styling and formatting --- src/App.css | 29 +++++++++++++++++++++++++++-- src/App.tsx | 4 ++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/App.css b/src/App.css index 7e1a4b22..4cb34533 100644 --- a/src/App.css +++ b/src/App.css @@ -4,7 +4,8 @@ html { font-size: 62.5%; } -body { +body, +ol { margin: 0; padding: 0; } @@ -23,7 +24,7 @@ body { } .timeDisplay { - margin: 0 0 50px; + margin: 0; font-size: 6rem; width: 500px; text-align: center; @@ -33,6 +34,7 @@ body { width: 500px; display: flex; justify-content: space-between; + margin: 10vh 0; } button { @@ -48,6 +50,26 @@ button:hover { background-color: red; } +.lapTimesContainer { + text-transform: uppercase; + width: 500px; + height: 35vh; + font-size: 2.6rem; + text-align: center; + overflow: scroll; + scrollbar-width: none; +} + +.lapTimesContainer ol { + list-style: none; + display: flex; + flex-direction: column-reverse; +} + +.lapTimesContainer li { + margin-bottom: 8px; +} + @media (max-width: 500px) { html { font-size: 40%; @@ -59,4 +81,7 @@ button:hover { button { margin-bottom: 30px; } + .lapTimesContainer { + width: 300px; + } } \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index d1ed325b..8e8a5d4a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -52,11 +52,11 @@ function App() { handleButtonClick={() => resetStopwatch()} />
-
+
    {lapTimes.map((lap, i) => { return ( -
  1. Lap {i + 1}: {formatTime(lap)}
  2. +
  3. Lap {i < 9 ? `0${i + 1}` : i + 1}: {formatTime(lap)}
  4. ) })}
From a537363a94908b92179e1eed8b42cf2939a71c25 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:42:27 -0500 Subject: [PATCH 20/33] fixed typo --- src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 8e8a5d4a..d67e23fd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -52,7 +52,7 @@ function App() { handleButtonClick={() => resetStopwatch()} />
-
+
    {lapTimes.map((lap, i) => { return ( From b8395dba020beb1ef221e9f60560c7ec98661de6 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 20:45:33 -0500 Subject: [PATCH 21/33] Formatting --- src/App.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index d67e23fd..0bb0cb03 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -56,7 +56,9 @@ function App() {
      {lapTimes.map((lap, i) => { return ( -
    1. Lap {i < 9 ? `0${i + 1}` : i + 1}: {formatTime(lap)}
    2. +
    3. + Lap {i < 9 ? `0${i + 1}` : i + 1}: {formatTime(lap)} +
    4. ) })}
    From 65ada08adb86fe9ed86817e300317e1eacc152b4 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 21:19:55 -0500 Subject: [PATCH 22/33] Updated Lap Times Logic --- src/App.tsx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0bb0cb03..a7c6971e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import './App.css'; -import React, { useState, useRef } from 'react'; +import React, { useState, useRef, useEffect } from 'react'; import StopWatch from './components/StopWatch'; import StopWatchButton from './components/StopWatchButton'; import formatTime from './utils/formatTime'; @@ -8,6 +8,17 @@ function App() { const intervalRef = useRef(null); const [timeElapsed, setTimeElapsed] = useState(0); const [lapTimes, setLapTimes] = useState([]); + const [lapTimesSum, setLapTimesSum] = useState(0); + const [isTiming, setIsTiming] = useState(false); + + useEffect(() => { + setLapTimesSum(lapTimes.reduce((a, b) => a + b, 0)); + }, [lapTimes]) + + // for debugging + useEffect(() => { + console.log(formatTime(lapTimesSum)); + }, [lapTimesSum]) const startStopwatch = () => { intervalRef.current @@ -21,7 +32,7 @@ function App() { } const recordLap = () => { - timeElapsed ? setLapTimes(prevLaps => [...prevLaps, timeElapsed]) : null; + timeElapsed ? setLapTimes(prevLaps => [...prevLaps, (timeElapsed - lapTimesSum)]) : null; } const resetStopwatch = () => { @@ -29,6 +40,7 @@ function App() { intervalRef.current = null; setTimeElapsed(0); setLapTimes([]); + setLapTimesSum(0); } return ( From 5238976498813a3f92c77d6a463881084ceacda3 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Sun, 28 Jan 2024 21:20:17 -0500 Subject: [PATCH 23/33] Button Styling --- src/App.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.css b/src/App.css index 4cb34533..5296713a 100644 --- a/src/App.css +++ b/src/App.css @@ -40,14 +40,14 @@ ol { button { padding: 20px; width: 100px; - cursor: pointer; border: none; border-radius: 2px; text-transform: uppercase; } -button:hover { +button:hover:enabled { background-color: red; + cursor: pointer; } .lapTimesContainer { From 606485e8af44a02ef02644c799debc00756d22f8 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:09:15 -0500 Subject: [PATCH 24/33] Stopwatch state functionality added the three stopwatch states and logic for disabling buttons depending on the stopwatch state --- src/App.tsx | 34 +++++++++++++++++------------- src/components/StopWatchButton.tsx | 17 +++++++++++---- 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index a7c6971e..8d54ca14 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -9,26 +9,25 @@ function App() { const [timeElapsed, setTimeElapsed] = useState(0); const [lapTimes, setLapTimes] = useState([]); const [lapTimesSum, setLapTimesSum] = useState(0); - const [isTiming, setIsTiming] = useState(false); + + type StopwatchState = 'reset' | 'started' | 'stopped'; + const [stopwatchState, setStopwatchState] = useState('reset'); useEffect(() => { setLapTimesSum(lapTimes.reduce((a, b) => a + b, 0)); }, [lapTimes]) - // for debugging - useEffect(() => { - console.log(formatTime(lapTimesSum)); - }, [lapTimesSum]) - const startStopwatch = () => { intervalRef.current ? null - : intervalRef.current = setInterval(() => setTimeElapsed(t => t + 10), 10); + : intervalRef.current = setInterval(() => setTimeElapsed(t => t + 10), 10); + setStopwatchState('started'); } const stopStopwatch = () => { clearInterval(intervalRef.current); intervalRef.current = null; + setStopwatchState('stopped'); } const recordLap = () => { @@ -41,6 +40,7 @@ function App() { setTimeElapsed(0); setLapTimes([]); setLapTimesSum(0); + setStopwatchState('reset'); } return ( @@ -48,20 +48,24 @@ function App() {
    startStopwatch()} + name='start' + stopwatchState={stopwatchState} + handleButtonClick={() => startStopwatch()} /> stopStopwatch()} + name='stop' + stopwatchState={stopwatchState} + handleButtonClick={() => stopStopwatch()} /> recordLap()} + name='lap' + stopwatchState={stopwatchState} + handleButtonClick={() => recordLap()} /> resetStopwatch()} + name='reset' + stopwatchState={stopwatchState} + handleButtonClick={() => resetStopwatch()} />
    diff --git a/src/components/StopWatchButton.tsx b/src/components/StopWatchButton.tsx index 9c036606..caf9fa37 100644 --- a/src/components/StopWatchButton.tsx +++ b/src/components/StopWatchButton.tsx @@ -1,13 +1,22 @@ import React from 'react'; interface buttonProps { - label: string; + name: string; + stopwatchState: string; handleButtonClick: () => void; } -const StopWatchButton = ({label, handleButtonClick}:buttonProps) => { - return( - +const StopWatchButton = ({ name, stopwatchState, handleButtonClick }: buttonProps) => { + if ( + (stopwatchState === 'reset' && name !== 'start') + || (stopwatchState === 'started' && name === 'start') + || (stopwatchState === 'stopped' && name === 'stop') + ) { + return ( + + ) + } else return ( + ) } From 080abacac872c634c6b152df7c58d2bb2da48c59 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:22:42 -0500 Subject: [PATCH 25/33] Refactor Moved lap times into separate component --- src/App.tsx | 14 ++------------ src/components/LapTimes.tsx | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 src/components/LapTimes.tsx diff --git a/src/App.tsx b/src/App.tsx index 8d54ca14..7fb75b0a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,7 +2,7 @@ import './App.css'; import React, { useState, useRef, useEffect } from 'react'; import StopWatch from './components/StopWatch'; import StopWatchButton from './components/StopWatchButton'; -import formatTime from './utils/formatTime'; +import LapTimes from './components/LapTimes'; function App() { const intervalRef = useRef(null); @@ -68,17 +68,7 @@ function App() { handleButtonClick={() => resetStopwatch()} />
    -
    -
      - {lapTimes.map((lap, i) => { - return ( -
    1. - Lap {i < 9 ? `0${i + 1}` : i + 1}: {formatTime(lap)} -
    2. - ) - })} -
    -
    +
) } diff --git a/src/components/LapTimes.tsx b/src/components/LapTimes.tsx new file mode 100644 index 00000000..8037deb0 --- /dev/null +++ b/src/components/LapTimes.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import formatTime from '../utils/formatTime'; + +interface lapTimesProps { + lapTimes: number[]; +} + +const LapTimes = ({ lapTimes }: lapTimesProps) => { + return ( +
+
    + {lapTimes.map((lap, i) => { + return ( +
  1. + Lap {i < 9 ? `0${i + 1}` : i + 1}: {formatTime(lap)} +
  2. + ) + })} +
+
+ ) +} + +export default LapTimes; \ No newline at end of file From 39a60d64c43393d04bd6be77c506a2cd35f2f18c Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:25:58 -0500 Subject: [PATCH 26/33] renamed component --- src/App.tsx | 4 ++-- src/components/{LapTimes.tsx => LapTimesList.tsx} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename src/components/{LapTimes.tsx => LapTimesList.tsx} (100%) diff --git a/src/App.tsx b/src/App.tsx index 7fb75b0a..cad30756 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,7 +2,7 @@ import './App.css'; import React, { useState, useRef, useEffect } from 'react'; import StopWatch from './components/StopWatch'; import StopWatchButton from './components/StopWatchButton'; -import LapTimes from './components/LapTimes'; +import LapTimesList from './components/LapTimesList'; function App() { const intervalRef = useRef(null); @@ -68,7 +68,7 @@ function App() { handleButtonClick={() => resetStopwatch()} />
- +
) } diff --git a/src/components/LapTimes.tsx b/src/components/LapTimesList.tsx similarity index 100% rename from src/components/LapTimes.tsx rename to src/components/LapTimesList.tsx From 71fe221ebf74cbce109e69ced3f8e7dab40242c8 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:37:01 -0500 Subject: [PATCH 27/33] Added type annotation --- src/utils/formatTime.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/formatTime.tsx b/src/utils/formatTime.tsx index 8f0f1f92..e4c92c8b 100644 --- a/src/utils/formatTime.tsx +++ b/src/utils/formatTime.tsx @@ -4,7 +4,7 @@ const formatTime = (timeInMilliseconds: number): string => { const milliseconds = Math.floor((timeInMilliseconds % 1000) / 10); const doubleDigits = (input: number) => { - const output = input < 10 ? `0${input}` : input; + const output: string|number = input < 10 ? `0${input}` : input; return output; }; From 1229ef8b0b8d62a874f720a0a6d4a8ffdbf4f943 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:19:42 -0500 Subject: [PATCH 28/33] Update App.tsx Added comments --- src/App.tsx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index cad30756..b0451e2c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,18 +5,25 @@ import StopWatchButton from './components/StopWatchButton'; import LapTimesList from './components/LapTimesList'; function App() { + // referencing the interval ID const intervalRef = useRef(null); + // the time displayed on the stopwatch const [timeElapsed, setTimeElapsed] = useState(0); + // an array to store the lap times const [lapTimes, setLapTimes] = useState([]); + // the sum of previous lap times, used to calculate each new lap time const [lapTimesSum, setLapTimesSum] = useState(0); - + // declaring possible stopwatch states type StopwatchState = 'reset' | 'started' | 'stopped'; const [stopwatchState, setStopwatchState] = useState('reset'); + // updating lapTimesSum each time a new lap is recorded useEffect(() => { setLapTimesSum(lapTimes.reduce((a, b) => a + b, 0)); }, [lapTimes]) + // if stopwatch is not running, start/resume timing and update stopwatch state + // timeElapsed is increased by 10 milliseconds at 10 millisecond intervals const startStopwatch = () => { intervalRef.current ? null @@ -24,16 +31,25 @@ function App() { setStopwatchState('started'); } + // stop the stopwatch and update state to 'stopped' + // interval will stop adding to timeElapsed when stopwatch is stopped + // but timeElapsed is not reset unless resetStopwatch is called const stopStopwatch = () => { clearInterval(intervalRef.current); intervalRef.current = null; setStopwatchState('stopped'); } + // if the stopwatch has been running, + // record a new lap and add it to the lapTimes array. + // calculate the new lap time by subtracting the + // sum of the previous lap times from the total time elapsed const recordLap = () => { timeElapsed ? setLapTimes(prevLaps => [...prevLaps, (timeElapsed - lapTimesSum)]) : null; } + // reset the stopwatch by clearing the interval ID and + // setting all states to their default values const resetStopwatch = () => { clearInterval(intervalRef.current); intervalRef.current = null; From 48b098e85717042d16a45202ac9bb9a6664db2a7 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:55:34 -0500 Subject: [PATCH 29/33] Added comments --- src/components/StopWatchButton.tsx | 6 ++++++ src/utils/formatTime.tsx | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/components/StopWatchButton.tsx b/src/components/StopWatchButton.tsx index caf9fa37..b74f0dc8 100644 --- a/src/components/StopWatchButton.tsx +++ b/src/components/StopWatchButton.tsx @@ -7,6 +7,11 @@ interface buttonProps { } const StopWatchButton = ({ name, stopwatchState, handleButtonClick }: buttonProps) => { + // certain buttons should be disabled depending on the stopwatch state + // disabled button conditions: + // disable all buttons except the start button when the stopwatch has been reset (default) + // disable only the start button when the stopwatch is running + // disable only the stop button when the stopwatch is stopped/paused if ( (stopwatchState === 'reset' && name !== 'start') || (stopwatchState === 'started' && name === 'start') @@ -15,6 +20,7 @@ const StopWatchButton = ({ name, stopwatchState, handleButtonClick }: buttonProp return ( ) + // if the none of the above conditions are met, render a normal/non-disabled button } else return ( ) diff --git a/src/utils/formatTime.tsx b/src/utils/formatTime.tsx index e4c92c8b..db31f0e1 100644 --- a/src/utils/formatTime.tsx +++ b/src/utils/formatTime.tsx @@ -1,13 +1,19 @@ const formatTime = (timeInMilliseconds: number): string => { + // converting the recorded time from a value in milliseconds + // into separate minutes, seconds, and milliseconds values const minutes = Math.floor((timeInMilliseconds % 3600000) / 60000); const seconds = Math.floor((timeInMilliseconds % 60000) / 1000); const milliseconds = Math.floor((timeInMilliseconds % 1000) / 10); + // ensuring that each time value is displayed in two digits + // so that the html element has consistent dimensions. + // if the input value is less than 10, output it with a zero in front of it const doubleDigits = (input: number) => { const output: string|number = input < 10 ? `0${input}` : input; return output; }; + // combining the separate time values into a user-friendly format return `${doubleDigits(minutes)}:${doubleDigits(seconds)}.${doubleDigits(milliseconds)}`; } From 2dcf836479928125ee19619497e4c4a91359b7af Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:39:04 -0500 Subject: [PATCH 30/33] First jest test set up testing file and passed first necessary test --- babel.config.js | 2 +- package-lock.json | 648 +++++++++++++++++++++++++++++ package.json | 2 + src/components/StopWatch.tsx | 2 +- src/components/StopWatchButton.tsx | 13 +- src/tests/App.test.tsx | 40 ++ 6 files changed, 703 insertions(+), 4 deletions(-) create mode 100644 src/tests/App.test.tsx diff --git a/babel.config.js b/babel.config.js index f21ab658..ebbec4a3 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,6 +1,6 @@ module.exports = { presets: [ - '@babel/preset-env', + ['@babel/preset-env', { targets: { node: 'current' } }], '@babel/preset-react', '@babel/preset-typescript', ], diff --git a/package-lock.json b/package-lock.json index ef8315e5..e916f9c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@babel/preset-env": "^7.23.8", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", + "@testing-library/jest-dom": "^6.3.0", "@testing-library/react": "^14.1.2", "@types/jest": "^29.5.11", "@types/node": "^16.18.30", @@ -29,6 +30,7 @@ "html-loader": "^4.2.0", "html-webpack-plugin": "^5.5.1", "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "react-test-renderer": "^18.2.0", "style-loader": "^3.3.2", "ts-jest": "^29.1.1", @@ -41,6 +43,12 @@ "webpack-obj-loader": "^1.0.4" } }, + "node_modules/@adobe/css-tools": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.3.tgz", + "integrity": "sha512-rE0Pygv0sEZ4vBWHlAgJLGDU7Pm8xoO6p3wsEceb7GYAjScrOHpEo8KK/eVkAcnSM+slAEtXjA2JpdjLp4fJQQ==", + "dev": true + }, "node_modules/@ampproject/remapping": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", @@ -2785,6 +2793,124 @@ "node": ">=8" } }, + "node_modules/@testing-library/jest-dom": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.3.0.tgz", + "integrity": "sha512-hJVIrkFizEQxoWsGBlycTcQhrpoCH4DhXfrnHFFXgkx3Xdm15zycsq5Ep+vpw4W8S0NJa8cxDHcuJib+1tEbhg==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.3.2", + "@babel/runtime": "^7.9.2", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.15", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + }, + "peerDependencies": { + "@jest/globals": ">= 28", + "@types/bun": "latest", + "@types/jest": ">= 28", + "jest": ">= 28", + "vitest": ">= 0.32" + }, + "peerDependenciesMeta": { + "@jest/globals": { + "optional": true + }, + "@types/bun": { + "optional": true + }, + "@types/jest": { + "optional": true + }, + "jest": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@testing-library/jest-dom/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + }, + "node_modules/@testing-library/jest-dom/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/@testing-library/react": { "version": "14.1.2", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.1.2.tgz", @@ -2803,6 +2929,15 @@ "react-dom": "^18.0.0" } }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -3034,6 +3169,17 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.12", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", @@ -3169,6 +3315,12 @@ "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==", "dev": true }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, "node_modules/@types/uglify-js": { "version": "3.17.1", "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.17.1.tgz", @@ -3430,6 +3582,13 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -3455,6 +3614,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, "node_modules/acorn-import-assertions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", @@ -3464,6 +3633,27 @@ "acorn": "^8" } }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3626,6 +3816,12 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -4278,6 +4474,18 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -4612,6 +4820,12 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -4624,12 +4838,50 @@ "node": ">=4" } }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", "dev": true }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4647,6 +4899,12 @@ } } }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, "node_modules/dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", @@ -4760,6 +5018,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4871,6 +5138,19 @@ } ] }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/domhandler": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", @@ -5060,6 +5340,36 @@ "node": ">=0.8.0" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -5444,6 +5754,20 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -5775,6 +6099,18 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/html-entities": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", @@ -5961,6 +6297,20 @@ "node": ">=8.0.0" } }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/http-proxy-middleware": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", @@ -5985,6 +6335,19 @@ } } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -6046,6 +6409,15 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -6320,6 +6692,12 @@ "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -7337,6 +7715,33 @@ "node": ">=8" } }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jest-environment-node": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", @@ -8643,6 +9048,51 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -8941,6 +9391,15 @@ "node": ">=6" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -9090,6 +9549,12 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", + "dev": true + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -9588,6 +10053,12 @@ "node": ">= 0.10" } }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -9628,6 +10099,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -9770,6 +10247,19 @@ "node": ">= 0.10" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -10001,6 +10491,18 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -10496,6 +10998,18 @@ "node": ">=6" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -10548,6 +11062,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -10689,6 +11209,33 @@ "node": ">=0.6" } }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/ts-jest": { "version": "29.1.1", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", @@ -10980,6 +11527,15 @@ "node": ">=4" } }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -11028,6 +11584,16 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -11087,6 +11653,18 @@ "node": ">= 0.8" } }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -11124,6 +11702,15 @@ "integrity": "sha512-4S6PQKwW8L2kPoZilvpNFYFFqZxfYvCWaGyVMXgETLfrqjbrD0RzNIlcAZPOiJx1/GqtNM9ibje47kSSd+Jn/w==", "dev": true }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/webpack": { "version": "5.88.2", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", @@ -11529,6 +12116,52 @@ "node": ">=0.8.0" } }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -11690,6 +12323,21 @@ } } }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index cf0d970a..a032fbec 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@babel/preset-env": "^7.23.8", "@babel/preset-react": "^7.23.3", "@babel/preset-typescript": "^7.23.3", + "@testing-library/jest-dom": "^6.3.0", "@testing-library/react": "^14.1.2", "@types/jest": "^29.5.11", "@types/node": "^16.18.30", @@ -32,6 +33,7 @@ "html-loader": "^4.2.0", "html-webpack-plugin": "^5.5.1", "jest": "^29.7.0", + "jest-environment-jsdom": "^29.7.0", "react-test-renderer": "^18.2.0", "style-loader": "^3.3.2", "ts-jest": "^29.1.1", diff --git a/src/components/StopWatch.tsx b/src/components/StopWatch.tsx index 04dd264a..8a06616d 100644 --- a/src/components/StopWatch.tsx +++ b/src/components/StopWatch.tsx @@ -7,7 +7,7 @@ interface stopWatchProps { const StopWatch = ({time} : stopWatchProps) => { return( -

{formatTime(time)}

+

{formatTime(time)}

) } diff --git a/src/components/StopWatchButton.tsx b/src/components/StopWatchButton.tsx index b74f0dc8..ba009916 100644 --- a/src/components/StopWatchButton.tsx +++ b/src/components/StopWatchButton.tsx @@ -18,11 +18,20 @@ const StopWatchButton = ({ name, stopwatchState, handleButtonClick }: buttonProp || (stopwatchState === 'stopped' && name === 'stop') ) { return ( - + ) // if the none of the above conditions are met, render a normal/non-disabled button } else return ( - + ) } diff --git a/src/tests/App.test.tsx b/src/tests/App.test.tsx new file mode 100644 index 00000000..cfc2301f --- /dev/null +++ b/src/tests/App.test.tsx @@ -0,0 +1,40 @@ +/** + * @jest-environment jsdom + */ + +import { render, screen, cleanup, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { act } from 'react-dom/test-utils'; +import React from 'react'; +import App from '../App'; + +// Arrange +// Act +// Assert + +beforeEach(() => { + // use fake timing functions to allow time manipulation for testing + jest.useFakeTimers(); +}); + +afterEach(() => { + // unmount elements rendered during tests + cleanup(); + // run all pending timers and switch back to real timers + jest.runOnlyPendingTimers() + jest.useRealTimers(); +}); + +test('stopwatch starts when user clicks start button', () => { + render(); + const startButton = screen.getByTestId('startButton'); + const stopwatchDisplay = screen.getByTestId('stopwatch'); + + fireEvent.click(startButton); + + act(() => { + jest.advanceTimersByTime(100); + }); + + expect(stopwatchDisplay).toHaveTextContent('00:00.10'); +}); \ No newline at end of file From 592a45caea0f0c3858fcda8ad6b2951882502acc Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Mon, 29 Jan 2024 21:48:11 -0500 Subject: [PATCH 31/33] updated jest config fixed webpack/jest/css import issue --- package-lock.json | 19 +++++++++++++++++++ package.json | 6 ++++++ 2 files changed, 25 insertions(+) diff --git a/package-lock.json b/package-lock.json index e916f9c1..5e4c264c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "file-loader": "^6.2.0", "html-loader": "^4.2.0", "html-webpack-plugin": "^5.5.1", + "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "react-test-renderer": "^18.2.0", @@ -5955,6 +5956,12 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "dev": true }, + "node_modules/harmony-reflect": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", + "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", + "dev": true + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -6381,6 +6388,18 @@ "postcss": "^8.1.0" } }, + "node_modules/identity-obj-proxy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", + "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", + "dev": true, + "dependencies": { + "harmony-reflect": "^1.4.6" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/import-local": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", diff --git a/package.json b/package.json index a032fbec..67f0763b 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "file-loader": "^6.2.0", "html-loader": "^4.2.0", "html-webpack-plugin": "^5.5.1", + "identity-obj-proxy": "^3.0.0", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "react-test-renderer": "^18.2.0", @@ -46,6 +47,11 @@ "webpack-obj-loader": "^1.0.4" }, "jest": { + "moduleNameMapper": { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": + "/__mocks__/fileMock.js", + "\\.(css|less)$": "identity-obj-proxy" + }, "transform": { "^.+\\.(js|jsx|ts|tsx)$": "babel-jest" } From 87616f3c0f685961ee64212e6a4ba4f8fc7cd334 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Mon, 29 Jan 2024 23:20:36 -0500 Subject: [PATCH 32/33] stop button test --- src/tests/App.test.tsx | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/tests/App.test.tsx b/src/tests/App.test.tsx index cfc2301f..c110f23f 100644 --- a/src/tests/App.test.tsx +++ b/src/tests/App.test.tsx @@ -2,16 +2,12 @@ * @jest-environment jsdom */ -import { render, screen, cleanup, fireEvent } from '@testing-library/react'; +import { render, screen, cleanup, fireEvent, waitFor } from '@testing-library/react'; import '@testing-library/jest-dom'; import { act } from 'react-dom/test-utils'; import React from 'react'; import App from '../App'; -// Arrange -// Act -// Assert - beforeEach(() => { // use fake timing functions to allow time manipulation for testing jest.useFakeTimers(); @@ -30,11 +26,34 @@ test('stopwatch starts when user clicks start button', () => { const startButton = screen.getByTestId('startButton'); const stopwatchDisplay = screen.getByTestId('stopwatch'); + // click the start button and wait 10 milliseconds fireEvent.click(startButton); - - act(() => { - jest.advanceTimersByTime(100); - }); + act(() => {jest.advanceTimersByTime(10);}); + + // stopwatch display should show that 10 milliseconds have passed + expect(stopwatchDisplay).toHaveTextContent('00:00.01'); +}); + +test('stopwatch stops when user clicks stop button', () => { + render(); + const startButton = screen.getByTestId('startButton'); + const stopButton = screen.getByTestId('stopButton'); + const stopwatchDisplay = screen.getByTestId('stopwatch'); + + // click the start button and wait 10000 milliseconds (10 seconds) + fireEvent.click(startButton); + act(() => {jest.advanceTimersByTime(10000);}); + + // click the stop button and wait another 10 seconds + fireEvent.click(stopButton); + act(() => {jest.advanceTimersByTime(10000);}); + + // stopwatch should show that only 10 seconds have been timed + // and not the additional 10 seconds after the stop button was clicked + expect(stopwatchDisplay).toHaveTextContent('00:10.00'); - expect(stopwatchDisplay).toHaveTextContent('00:00.10'); + // this test is passing but I'm not sure if it is truly testing the + // functionality of the stop button correctly. I tried to implement + // a better solution using things like mock timers and waitFor + // but I ran out of time so I reverted back to my first iteration. }); \ No newline at end of file From 4b250d2c0c85585b4498a4851df6b93a61faef40 Mon Sep 17 00:00:00 2001 From: Jake Spodek <68516673+jakespodek@users.noreply.github.com> Date: Mon, 29 Jan 2024 23:43:06 -0500 Subject: [PATCH 33/33] more tests --- src/components/LapTimesList.tsx | 2 +- src/tests/App.test.tsx | 57 ++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/components/LapTimesList.tsx b/src/components/LapTimesList.tsx index 8037deb0..0bcdafdb 100644 --- a/src/components/LapTimesList.tsx +++ b/src/components/LapTimesList.tsx @@ -8,7 +8,7 @@ interface lapTimesProps { const LapTimes = ({ lapTimes }: lapTimesProps) => { return (
-
    +
      {lapTimes.map((lap, i) => { return (
    1. diff --git a/src/tests/App.test.tsx b/src/tests/App.test.tsx index c110f23f..8c2e4e5f 100644 --- a/src/tests/App.test.tsx +++ b/src/tests/App.test.tsx @@ -46,6 +46,8 @@ test('stopwatch stops when user clicks stop button', () => { // click the stop button and wait another 10 seconds fireEvent.click(stopButton); + // I think this line might not actually be doing anything but + // I don't have enough time to figure that out act(() => {jest.advanceTimersByTime(10000);}); // stopwatch should show that only 10 seconds have been timed @@ -56,4 +58,57 @@ test('stopwatch stops when user clicks stop button', () => { // functionality of the stop button correctly. I tried to implement // a better solution using things like mock timers and waitFor // but I ran out of time so I reverted back to my first iteration. -}); \ No newline at end of file +}); + +test('lap button records a lap time every time it is clicked', () => { + render(); + const startButton = screen.getByTestId('startButton'); + const lapButton = screen.getByTestId('lapButton'); + const lapTimes = screen.getByTestId('lapTimes').children; + + // click the start button and wait 10000 milliseconds (10 seconds) + fireEvent.click(startButton); + act(() => { jest.advanceTimersByTime(10000); }); + + // click the lap button three times + fireEvent.click(lapButton); + fireEvent.click(lapButton); + fireEvent.click(lapButton); + + // the lap times list should have a length of 3 + expect(lapTimes.length).toBe(3); +}); + +test('reset button resets the stopwatch timer and recorded laps', () => { + render(); + const startButton = screen.getByTestId('startButton'); + const stopwatchDisplay = screen.getByTestId('stopwatch'); + const lapButton = screen.getByTestId('lapButton'); + const lapTimes = screen.getByTestId('lapTimes').children; + const resetButton = screen.getByTestId('resetButton'); + + // click the start button and wait 10000 milliseconds (10 seconds) + fireEvent.click(startButton); + act(() => { jest.advanceTimersByTime(10000); }); + + // stopwatch should display 10 seconds + expect(stopwatchDisplay).toHaveTextContent('00:10.00'); + + // click the lap button three times + fireEvent.click(lapButton); + fireEvent.click(lapButton); + fireEvent.click(lapButton); + + // the lap times list should have a length of 3 + expect(lapTimes.length).toBe(3); + + // click reset button + fireEvent.click(resetButton); + + // the lap times list should now have a length of 0 + expect(lapTimes.length).toBe(0); + // and the stopwatch should be reset to show all 0's + expect(stopwatchDisplay).toHaveTextContent('00:00.00'); +}); + +// all tests are passing \ No newline at end of file