vercel / next.js

The React Framework
https://nextjs.org
MIT License
126.78k stars 26.95k forks source link

Jest Timezone discrepancy between running single test vs suite (next/jest) #41705

Open littlejon opened 2 years ago

littlejon commented 2 years ago

Verify canary release

Provide environment information

Operating System:
  Platform: win32
  Arch: x64
  Version: Windows 10 Pro
Binaries:
  Node: 16.18.0
  npm: 8.19.2
  Yarn: 1.22.19
  pnpm: 7.13.5
Relevant packages:
  next: 12.3.2-canary.35
  eslint-config-next: N/A
  react: 18.2.0
  react-dom: 18.2.0

warn - Latest canary version not detected, detected: "12.3.2-canary.35", newest: "12.3.2-canary.37". Please try the latest canary version (npm install next@canary) to confirm the issue still exists before creating a new issue. Read more - https://nextjs.org/docs/messages/opening-an-issue

What browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

Describe the Bug

Using the globalSetup config I have the TZ env variable set to UTC.

globalSetup.ts

export const setup = async () => {
  process.env.TZ = "UTC";
};

export default setup;

When running a suite of tests, the TZ is honored. When running a single test, the TZ is ignored and the test fails.

I have setup a reproduction repo with two jest configs (one using the Rust compiler, and one setup to opt-out). This issue is only seen when using the Rust compiler configured via next/jest.

Test content

const date = "2021-10-20T13:06:05Z";
expect(new Date(date).getHours()).toBe(13);

Expected Behavior

Consistent test results when running an individual test and when running the whole test suite.

Link to reproduction

https://github.com/littlejon/jest-next-time-zone-issue

To Reproduce

Install

git clone https://github.com/littlejon/jest-next-time-zone-issue.git
cd jest-next-time-zone-issue
pnpm install

Run Tests

pnpm jest-babel-all
pnpm jest-babel-one
pnpm jest-next-all
pnpm jest-next-one

jest-babel-all

PS C:\Code\testing\repro> pnpm jest-babel-all

> @ jest-babel-all C:\Code\testing\repro
> jest --no-cache --config ./jest.config.babel.js

 PASS  lib/formatDate.test.ts
  ● Console

    console.log
      Timezone Offset: 0

      at Object.log (lib/formatDate.test.ts:8:15)

 PASS  lib/randomFile.test.ts

Test Suites: 2 passed, 2 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.529 s
Ran all test suites.

jest-babel-one

PS C:\Code\testing\repro> pnpm jest-babel-one

> @ jest-babel-one C:\Code\testing\repro
> jest --no-cache --config ./jest.config.babel.js lib/formatDate.test.ts

  console.log
    Timezone Offset: 0

      at Object.log (lib/formatDate.test.ts:8:13)

 PASS  lib/formatDate.test.ts
  ISO Date
    √ returns correct hour value (32 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.605 s
Ran all test suites matching /lib\\formatDate.test.ts/i.

jest-next-all

PS C:\Code\testing\repro> pnpm jest-next-all

> @ jest-next-all C:\Code\testing\repro
> jest --no-cache --config ./jest.config.next.js

 PASS  lib/formatDate.test.ts
  ● Console

    console.log
      Timezone Offset: 0

      at Object.log (lib/formatDate.test.ts:8:15)

 PASS  lib/randomFile.test.ts

Test Suites: 2 passed, 2 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.006 s
Ran all test suites.

jest-next-one

PS C:\Code\testing\repro> pnpm jest-next-one

> @ jest-next-one C:\Code\testing\repro
> jest --no-cache --config ./jest.config.next.js lib/formatDate.test.ts

  console.log
    Timezone Offset: -600

      at Object.log (lib/formatDate.test.ts:8:13)

 FAIL  lib/formatDate.test.ts
  ISO Date
    × returns correct hour value (21 ms)

  ● ISO Date › returns correct hour value

    expect(received).toBe(expected) // Object.is equality

    Expected: 13
    Received: 23

       8 |     console.log(`Timezone Offset: ${new Date().getTimezoneOffset()}`);
       9 |
    > 10 |     expect(new Date(date).getHours()).toBe(13);
         |                                       ^
      11 |   });
      12 | });
      13 |

      at Object.toBe (lib/formatDate.test.ts:10:39)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        0.433 s
Ran all test suites matching /lib\\formatDate.test.ts/i.
 ELIFECYCLE  Command failed with exit code 1.
littlejon commented 2 years ago

I have done some more testing and found the issue isn't directly connected to running a single test vs the suite, but more to do with the number of workers involved.

Scenario Num of Workers Result Notes
jest-next-all Jest Default (number of cores available minus one) PASS
jest-next-one Jest Default (number of cores available minus one) FAIL Fails as one test = one worker
jest-next-all-w1 --maxWorkers=1 FAIL one worker
jest-next-all-w2 --maxWorkers=2 PASS
jest-next-all-i --runInBand FAIL All tests run serially in the current process (no worker threads)

The above test scenarios when run against the babel config all pass.

I have tried tracing my way through the code in packages\next\build and ran out of skill to debug further. I can see from the cached output between the two configs that the code is being transpiled slightly different.

The only functional difference I can see is SWC adds Object.defineProperty(exports, "__esModule", { value: true }); to the transpiled test file whereas Babel doesn't. Although I don't have a deep enough understanding to know if this would make the difference.

Any pointers on how to debug this further?

globalSetup.ts - SWC

909b94d6cb9563490b1987711d427323
/* eslint-disable import/no-anonymous-default-export */ "use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: all[name]
    });
}
_export(exports, {
    setup: ()=>setup,
    default: ()=>_default
});
const setup = async ()=>{
    process.env.TZ = "UTC";
};
const _default = setup;

//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkM6XFxDb2RlXFx6elRlc3RpbmdcXGplc3QtbmV4dC10aW1lLXpvbmUtaXNzdWVcXGdsb2JhbFNldHVwLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1hbm9ueW1vdXMtZGVmYXVsdC1leHBvcnQgKi9cclxuZXhwb3J0IGNvbnN0IHNldHVwID0gYXN5bmMgKCkgPT4ge1xyXG4gIHByb2Nlc3MuZW52LlRaID0gXCJVVENcIjtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHNldHVwO1xyXG4iXSwibmFtZXMiOlsic2V0dXAiLCJwcm9jZXNzIiwiZW52IiwiVFoiXSwibWFwcGluZ3MiOiJBQUFBLHFEQUFxRCxHQUNyRDs7Ozs7Ozs7Ozs7SUFBYUEsS0FBSyxNQUFMQTtJQUliLE9BQXFCLE1BQXJCOztBQUpPLE1BQU1BLFFBQVEsVUFBWTtJQUMvQkMsUUFBUUMsR0FBRyxDQUFDQyxFQUFFLEdBQUc7QUFDbkI7TUFFQSxXQUFlSCJ9

globalSetup.ts - Babel

426d5cefb5cb6353e2d6ebeb1e635ba9
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.setup = exports.default = void 0;
/* eslint-disable import/no-anonymous-default-export */
const setup = async () => {
  process.env.TZ = "UTC";
};
exports.setup = setup;
var _default = setup;
exports.default = _default;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJzZXR1cCIsInByb2Nlc3MiLCJlbnYiLCJUWiJdLCJzb3VyY2VzIjpbImdsb2JhbFNldHVwLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIGltcG9ydC9uby1hbm9ueW1vdXMtZGVmYXVsdC1leHBvcnQgKi9cclxuZXhwb3J0IGNvbnN0IHNldHVwID0gYXN5bmMgKCkgPT4ge1xyXG4gIHByb2Nlc3MuZW52LlRaID0gXCJVVENcIjtcclxufTtcclxuXHJcbmV4cG9ydCBkZWZhdWx0IHNldHVwO1xyXG4iXSwibWFwcGluZ3MiOiI7Ozs7OztBQUFBO0FBQ08sTUFBTUEsS0FBSyxHQUFHLFlBQVk7RUFDL0JDLE9BQU8sQ0FBQ0MsR0FBRyxDQUFDQyxFQUFFLEdBQUcsS0FBSztBQUN4QixDQUFDO0FBQUM7QUFBQSxlQUVhSCxLQUFLO0FBQUEifQ==

formatDate.test.tsx - SWC

7229085341935b999a1c427b6e753607
"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
const _globals = require("@jest/globals");
(0, _globals.describe)("ISO Date", ()=>{
    const date = "2021-10-20T13:06:05Z";
    (0, _globals.it)("returns correct hour value", ()=>{
        console.log(`Timezone Offset: ${new Date().getTimezoneOffset()}`);
        (0, _globals.expect)(new Date(date).getHours()).toBe(13);
    });
});

//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkM6XFxDb2RlXFx6elRlc3RpbmdcXGplc3QtbmV4dC10aW1lLXpvbmUtaXNzdWVcXGxpYlxcZm9ybWF0RGF0ZS50ZXN0LnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGRlc2NyaWJlLCBleHBlY3QsIGl0IH0gZnJvbSBcIkBqZXN0L2dsb2JhbHNcIjtcclxuXHJcblxyXG5kZXNjcmliZShcIklTTyBEYXRlXCIsICgpID0+IHtcclxuICBjb25zdCBkYXRlID0gXCIyMDIxLTEwLTIwVDEzOjA2OjA1WlwiO1xyXG5cclxuICBpdChcInJldHVybnMgY29ycmVjdCBob3VyIHZhbHVlXCIsICgpID0+IHtcclxuICAgIGNvbnNvbGUubG9nKGBUaW1lem9uZSBPZmZzZXQ6ICR7bmV3IERhdGUoKS5nZXRUaW1lem9uZU9mZnNldCgpfWApO1xyXG5cclxuICAgIGV4cGVjdChuZXcgRGF0ZShkYXRlKS5nZXRIb3VycygpKS50b0JlKDEzKTtcclxuICB9KTtcclxufSk7XHJcblxyXG4iXSwibmFtZXMiOlsiZGVzY3JpYmUiLCJkYXRlIiwiaXQiLCJjb25zb2xlIiwibG9nIiwiRGF0ZSIsImdldFRpbWV6b25lT2Zmc2V0IiwiZXhwZWN0IiwiZ2V0SG91cnMiLCJ0b0JlIl0sIm1hcHBpbmdzIjoiQUFBQTs7Ozt5QkFBcUM7QUFHckNBLElBQUFBLGlCQUFRLEVBQUMsWUFBWSxJQUFNO0lBQ3pCLE1BQU1DLE9BQU87SUFFYkMsSUFBQUEsV0FBRSxFQUFDLDhCQUE4QixJQUFNO1FBQ3JDQyxRQUFRQyxHQUFHLENBQUMsQ0FBQyxpQkFBaUIsRUFBRSxJQUFJQyxPQUFPQyxpQkFBaUIsR0FBRyxDQUFDO1FBRWhFQyxJQUFBQSxlQUFNLEVBQUMsSUFBSUYsS0FBS0osTUFBTU8sUUFBUSxJQUFJQyxJQUFJLENBQUM7SUFDekM7QUFDRiJ9

formatDate.test.tsx - Babel

98aeaa1f1992f1589af8d940e7ebc0b9
"use strict";

var _globals = require("@jest/globals");
(0, _globals.describe)("ISO Date", () => {
  const date = "2021-10-20T13:06:05Z";
  (0, _globals.it)("returns correct hour value", () => {
    console.log(`Timezone Offset: ${new Date().getTimezoneOffset()}`);
    (0, _globals.expect)(new Date(date).getHours()).toBe(13);
  });
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJkZXNjcmliZSIsImRhdGUiLCJpdCIsImNvbnNvbGUiLCJsb2ciLCJEYXRlIiwiZ2V0VGltZXpvbmVPZmZzZXQiLCJleHBlY3QiLCJnZXRIb3VycyIsInRvQmUiXSwic291cmNlcyI6WyJmb3JtYXREYXRlLnRlc3QudHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZGVzY3JpYmUsIGV4cGVjdCwgaXQgfSBmcm9tIFwiQGplc3QvZ2xvYmFsc1wiO1xyXG5cclxuXHJcbmRlc2NyaWJlKFwiSVNPIERhdGVcIiwgKCkgPT4ge1xyXG4gIGNvbnN0IGRhdGUgPSBcIjIwMjEtMTAtMjBUMTM6MDY6MDVaXCI7XHJcblxyXG4gIGl0KFwicmV0dXJucyBjb3JyZWN0IGhvdXIgdmFsdWVcIiwgKCkgPT4ge1xyXG4gICAgY29uc29sZS5sb2coYFRpbWV6b25lIE9mZnNldDogJHtuZXcgRGF0ZSgpLmdldFRpbWV6b25lT2Zmc2V0KCl9YCk7XHJcblxyXG4gICAgZXhwZWN0KG5ldyBEYXRlKGRhdGUpLmdldEhvdXJzKCkpLnRvQmUoMTMpO1xyXG4gIH0pO1xyXG59KTtcclxuXHJcbiJdLCJtYXBwaW5ncyI6Ijs7QUFBQTtBQUdBLElBQUFBLGlCQUFRLEVBQUMsVUFBVSxFQUFFLE1BQU07RUFDekIsTUFBTUMsSUFBSSxHQUFHLHNCQUFzQjtFQUVuQyxJQUFBQyxXQUFFLEVBQUMsNEJBQTRCLEVBQUUsTUFBTTtJQUNyQ0MsT0FBTyxDQUFDQyxHQUFHLENBQUUsb0JBQW1CLElBQUlDLElBQUksRUFBRSxDQUFDQyxpQkFBaUIsRUFBRyxFQUFDLENBQUM7SUFFakUsSUFBQUMsZUFBTSxFQUFDLElBQUlGLElBQUksQ0FBQ0osSUFBSSxDQUFDLENBQUNPLFFBQVEsRUFBRSxDQUFDLENBQUNDLElBQUksQ0FBQyxFQUFFLENBQUM7RUFDNUMsQ0FBQyxDQUFDO0FBQ0osQ0FBQyxDQUFDIn0=