Компоненты, использующие объекты Date, создают разные моментальные снимки в разных часовых поясах
Я использую Enzyme с энзимом к json для Jest тестирования снимков моих компонентов React. Я тестирую мелкие снимки компонента DateRange
, который отображает поле отображения с текущим диапазоном (например, 5/20/2016 - 7/18/2016
) и два компонента DateInput
, которые позволяют выбрать значение Date
. Это означает, что мой снимок содержит Date
, который я передаю компоненту как в подпорках DateInput
, так и в текстовом представлении, которое он разрешает сам. В моем тесте я создаю несколько фиксированных дат, используя new Date(1995, 4, 23)
.
Когда я запускаю тест в разных часовых поясах, это создает разные снимки, потому что конструктор Date(year, month, ...)
создает дату в местном часовом поясе. Например. использование new Date()
создает эту разницу в снимке между запусками в моем местном часовом поясе и на нашем CI-сервере.
- value={1995-05-22T22:00:00.000Z}
+ value={1995-05-23T00:00:00.000Z}
Я пытался удалить смещение часового пояса из дат, но затем снимок отличался значением поля отображения, где используется локальное представление, зависящее от часового пояса.
- value={5/20/2016 - 7/18/2016}
+ value={5/19/2016 - 7/17/2016}
Как я могу заставить мои тесты создавать одинаковые Date
на снимках независимо от часового пояса, в котором они выполняются?
Ответы
Ответ 1
Я боролся с этим часами/днями, и только это работало для меня:
1) В вашем тесте:
Date.now = jest.fn(() => new Date(Date.UTC(2017, 7, 9, 8)).valueOf())
2) Затем измените TZ
env var перед запуском ваших тестов. Итак, скрипт в моем package.json:
-
(Только для Mac и Linux)
"test": "TZ=America/New_York react-scripts test --env=jsdom",
-
(Windows)
"test": "set TZ=America/New_York && react-scripts test --env=jsdom",
Ответ 2
В итоге у меня было решение, состоящее из двух частей.
-
Никогда не создавайте объекты Date
в тестах в зависимости от часового пояса. Если вы не хотите использовать временные метки непосредственно для считывания тестового кода, используйте Date.UTC
, например.
new Date(Date.UTC(1995, 4, 23))
- Измените формат даты, используемый для преобразования
Date
в отображаемые значения, чтобы он возвращал независимое от часового пояса представление, например. используйте Date::toISOString()
. К счастью, это было легко в моем случае, поскольку мне просто нужно было высмеять функцию formatDate
в моем модуле локализации. Это может быть сложнее, если компонент каким-то образом превращает Date
в строки самостоятельно.
Прежде чем я пришел к вышеуказанному решению, я попытался как-то изменить способ создания моментальных снимков. Это было уродливо, потому что фермент-json сохраняет локальную копию toISOString()
, поэтому мне пришлось использовать _.cloneDeepWith
и изменить все Date
s. В любом случае это не сработало для меня, потому что в моих тестах также содержались случаи создания Date
из временных меток (компонент довольно сложный, чем описан выше) и взаимодействия между теми и датами, которые я создавал в тестах в явном виде. Поэтому я сначала должен был убедиться, что все мои определения даты относятся к одному и тому же часовому поясу, а остальные следуют.
Обновление (11/3/2017): когда я недавно проверил enzyme-to-json
, мне не удалось найти локальную экономию toISOString()
, поэтому, возможно, это уже не проблема, и ее можно было высмеять. Я не смог найти его в истории, хотя, возможно, я просто неправильно заметил, какая библиотека это сделала. Испытайте свою собственную опасность:)
Ответ 3
Я закончил тем, что обошел это, издеваясь над прототипом toLocaleString
(или любого другого метода toString, который вы используете). Используя sinon
, я сделал:
var toLocaleString;
beforeAll(() => {
toLocaleString = sinon.stub(Date.prototype, 'toLocaleString', () => 'fake time')
})
afterAll(() => {
toLocaleString.restore()
})
Таким образом, если вы генерируете строки прямо из объекта Date
, вы все еще в порядке.
Ответ 4
Я наткнулся на самое простое решение - просто передайте Date
строку, и часовой пояс не будет иметь значения, если вам нужно только правильное представление строки.
Date("2022-02-22")
всегда будет представлять '2022-02-22T00:00:00.000Z'
в часовом поясе, в котором вы работаете, узел.
Это будет выглядеть следующим образом в вашем блоке, который вы запускаете перед тестами, если дата генерируется автоматически с помощью new Date()
:
const baseTime = new Date("2022-02-22");
spyOn(global, 'Date').and.returnValue(baseTime);
Ответ 5
Если вы используете конструктор new Date()
вместо Date.now, вы можете сделать следующее:
const RealDate = Date;
beforeEach(() => {
// @ts-ignore
global.Date = class extends RealDate {
constructor() {
super();
return new RealDate("2016");
}
};
})
afterEach(() => {
global.Date = RealDate;
});
Этот вопрос обязательно нужно посетить, если вы здесь.