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, а не за процесами, оскільки саме ця одиниця тут існує. Застосунок, що простоює й ніколи не питає час, чесно дає не визначено — він не читав годинник, тож не можна стверджувати, чи побачив би він правильний. Що означають вердикти →

Спробуйте