epicweb-dev / testing-fundamentals

Learn the fundamentals of automated testing in TypeScript.
https://www.epicweb.dev/workshops/testing-fundamentals
Other
108 stars 14 forks source link

02/04 - Mocked date gives wrong day if time not explicitly set to be after 00:00 #14

Closed isimmons closed 2 months ago

isimmons commented 5 months ago

In exercise 02 section 04 "Hooks" my test failed. When mocking the date it appears to be defaulting to "2024-01-01:00:00" but the day doesn't actually start until 1 second after that so what I get is "2023-12-31" if I console.log it out.

I fixed it in my code by explicitly setting the time to 1AM

beforeAll(() => {
    globalThis.Date = new Proxy(globalThis.Date, {
        construct: () => new OriginalDate('2024-01-01:01:00'),
    })
})

I just realized this is a problem on every lesson after this one. Is it something wrong on my end?

Windows 10, node 20

kettanaito commented 5 months ago

Hi, @isimmons. Thanks for sharing this!

I've encountered a similar issue when giving the live version of this workshop, where the culprit was the omission of the explicit locale when calling .toLocaleDateString(). I've then defaulted it to en-US, which should produce the same date for everyone:

https://github.com/epicweb-dev/testing-fundamentals/blob/753655ba11132af4885aa702b4bea972aa9d60c7/exercises/02.test-structure/04.solution.hooks/greet.ts#L2

I'm trying to reproduce your issue, this is where I got:

--- a/exercises/02.test-structure/04.solution.hooks/greet.test.ts
+++ b/exercises/02.test-structure/04.solution.hooks/greet.test.ts
@@ -6,6 +6,8 @@ beforeAll(() => {
        globalThis.Date = new Proxy(globalThis.Date, {
                construct: () => new OriginalDate('2024-01-01'),
        })
+
+       console.log(new OriginalDate('2024-01-01'))
 })

 afterAll(() => {

This prints the following for me:

2024-01-01T00:00:00.000Z

Does this print 2023-12-31 for you?

isimmons commented 5 months ago

Yes Sir, here is my output.

    const date = new OriginalDate('2024-01-01')
    console.log(date) // 2024-01-01T00:00:00.000Z
    console.log(date.toLocaleDateString()) // 12/31/2023
    console.log(date.toLocaleDateString('en-US', { weekday: 'long' })) // Sunday

    const anotherDate = new OriginalDate('2024-01-01:00:00:00.001')
    console.log(anotherDate) // 2024-01-01T00:00:00.001Z
    console.log(anotherDate.toLocaleDateString()) // 1/1/2024
    console.log(anotherDate.toLocaleDateString('en-US', { weekday: 'long' })) // Monday
kettanaito commented 2 months ago

@isimmons, hi! Sorry for getting so late to you.

Can you tell me if this fixes your issue?

--- a/exercises/02.test-structure/04.solution.hooks/greet.ts
+++ b/exercises/02.test-structure/04.solution.hooks/greet.ts
@@ -1,5 +1,8 @@
 export function greet(name: string) {
-       const weekday = new Date().toLocaleDateString('en-US', { weekday: 'long' })
+       const weekday = new Date().toLocaleDateString('en-US', {
+               weekday: 'long',
+               timeZone: 'UTC',
+       })

        return `Hello, ${name}! Happy, ${weekday}.`
 }

It's a change you need to do in greet.ts: add a timeZone property to .toLocaleDateString() and set its value to "UTC".

kettanaito commented 2 months ago

It's important to mention that it's likely a timezone issue and it gets triggered by setting explicit time on the mock date. Notice that the exercises never provide the time, only the date:

beforeAll(() => {
  globalThis.Date = new Proxy(globalThis.Date, {
    construct: () => new OriginalDate('2024-01-01'),
  })
})

But if you try to provide a date+time string, it will indeed create a previous day (1h before) even in raw Node.js:

new Date('2024-01-01 00:00:00.000')
// 2023-12-31T23:00:00.000Z

I suspect this is because Node.js has a default timezone that's not UTC. There are two ways to fix this:

  1. Provide an explicit timezone within the date (notice the Z at the end, which stands for UTC):
new Date('2024-01-01 00:00:00.000Z')
// 2024-01-01T00:00:00.000Z
  1. Provide an explicit timezone on the test process itself:
TZ=UTC npx tsx ...
isimmons commented 2 months ago

@kettanaito yes setting the timezone fixes the issue.

Actually for me, not setting an explicit time in the test, it fails. Setting the time explicit 00:00:00.0001 was my workaround to make it pass.

But the timezone option in greet.ts fixes it without any need for explicit time.

Thanks