Chrono Mock
ЯзыкРусский

Подмена даты в приложении на Electron

Поищите это, и каждый ответ будет о том, как менять собственный код: замокать объект Date, использовать фальшивые таймеры своего тестового фреймворка. Ничто из этого не помогает, когда у вас готовое собранное приложение. Эта программа запускает то приложение, которое у вас уже есть.

Почему внедрение до него не достаёт

Этот режим существует из-за конкретной неудачи. Таймер Pomodoro на Electron запустили в сессии x60, и его обратный отсчёт вообще не сдвинулся. Разбор пакета приложения показал две независимые причины, и ни у одной не было решения на нативной стороне:

  • Обратный отсчёт не читал часы. Это был счётчик, увеличиваемый setInterval, работающий внутри Web Worker. Перехватывать было нечего: нет вызова времени, потому что приложение никогда не спрашивало, который час.
  • Этот worker живёт в изолированном процессе рендерера, то есть именно там, куда библиотека, внедрённая снаружи, не попадает.

Поэтому механизм на стороне Windows здесь не поможет, и ответ — обращаться вместо этого к собственному движку JavaScript приложения.

Что происходит вместо этого

Приложение запускается с портом отладки и чистым одноразовым профилем, а его собственные API времени переводятся на часы сессии через протокол DevTools — Date.now, конструктор new Date(), performance.now, setTimeout и setInterval.

Подмена устанавливается в каждый контекст, который открывает приложение, включая его Web Worker — именно там обычно и оказывается таймер, и это единственное место, куда нативное внедрение никогда не достало бы.

В том, как вы им управляете, ничего не меняется. Та же команда, то же окно, та же панель сессии с двумя часами и вердиктом.

# Та же команда, что и для нативного приложения — режим определяется сам
chrono run "%LOCALAPPDATA%\Programs\your-app\YourApp.exe" --at 2038-01-01T00:00:00 --mode x60

Два ограничения, о которых стоит знать до начала

Запуск навязчив, и это намеренно говорится вслух.

Приложение запускается с открытым портом удалённой отладки и новым изолированным профилем в %TEMP% — оно не работает на вашем настоящем профиле и это не та копия, что уже висит у вас в области уведомлений. Профиль удаляется по окончании сессии, а если это не удалось, сводка сессии сообщает об этом, а не оставляет его тихо лежать.

Передача вашего собственного --user-data-dir или --remote-debugging-port отклоняется, а не переопределяется, потому что очевидную оплошность — направить сессию на ваш настоящий профиль браузера — допускать не стоит.

Страницы читают часовой пояс сессии.

Каждая страница и каждый воркер переводится на часовой пояс сессии, фиксированное смещение без перехода на летнее время, и проверяет смещение, которое читает, поэтому страница, прочитавшая другой пояс, попадает в отчёт. Два ограничения: Intl называет этот пояс смещением вроде GMT+02:00, а не городом, а главный процесс приложения Electron выполняет JavaScript в Node, куда сессия не доходит, поэтому он остаётся на реальных часах - отчёт об этом говорит.

Ещё одно, что удивляет людей: интервал, уже поставленный в расписание, сохраняет прежний темп, когда вы меняете скорость посреди сессии. Date.now, new Date() и performance.now реагируют сразу, а работающий setInterval — нет, и сессия предупреждает об этом, а не позволяет сделать вывод, что изменение ничего не сделало.

Насколько хорошо это проверено

Экспериментально, и у этого слова здесь определённый смысл: оно работает на тех целях, под которые создавалось, не проверялось широко, а встроенный аудит — ваш источник истины, а не эта страница. Проверено вручную, а не автоматическим набором, на настоящем приложении Electron в 64-разрядной Windows.

Покрытие сообщается по контекстам JavaScript, а не по процессам, поскольку именно эта единица здесь существует. Приложение, которое простаивает и никогда не спрашивает время, честно даёт не определено — оно не читало часы, поэтому нельзя утверждать, увидело бы оно правильные. Что означают вердикты →

Попробуйте