Chrono Mock

Podmiana daty w aplikacji Electron

Poszukaj tego, a każda odpowiedź dotyczy zmiany własnego kodu - podmień obiekt Date, użyj sztucznych timerów z frameworka testowego. Nic z tego nie pomaga, gdy masz gotową, zbudowaną aplikację. To uruchamia tę, którą już masz.

Dlaczego wstrzyknięcie tam nie sięga

Ten tryb powstał z konkretnej porażki. Minutnik Pomodoro zbudowany na Electronie został uruchomiony w sesji x60 i jego odliczanie nie ruszyło się wcale. Rozłożenie paczki aplikacji pokazało dwa niezależne powody i żaden nie miał naprawy po stronie natywnej:

  • Odliczanie w ogóle nie czytało zegara. Był to licznik zwiększany przez setInterval, działający wewnątrz Web Workera. Nie było wywołania czasu do przechwycenia, bo aplikacja nigdy nie pytała, która godzina.
  • Ten worker żyje w piaskowanym procesie renderera, czyli dokładnie tam, gdzie biblioteka wstrzyknięta z zewnątrz się nie dostaje.

Mechanizm windowsowy nie może więc tu pomóc, a odpowiedzią jest rozmowa z własnym silnikiem JavaScriptu aplikacji.

Co dzieje się zamiast tego

Aplikacja jest uruchamiana z portem debugowania i czystym, jednorazowym profilem, a jej własne API czasu zostają przestawione na zegar sesji przez protokół DevTools - Date.now, konstruktor new Date(), performance.now, setTimeout i setInterval.

Nadpisanie trafia do każdego kontekstu, jaki aplikacja otwiera, łącznie z Web Workerami - czyli tam, gdzie zwykle okazuje się mieszkać timer, i w to jedno miejsce, do którego natywne wstrzyknięcie i tak by nie dotarło.

Sposób sterowania nie zmienia się w niczym. Ta sama komenda, to samo okno, ten sam panel sesji z dwoma zegarami i werdyktem.

# Ta sama komenda co dla aplikacji natywnej - tryb jest wykrywany
chrono run "%LOCALAPPDATA%\Programs\your-app\YourApp.exe" --at 2038-01-01T00:00:00 --mode x60

Dwa ograniczenia, o których warto wiedzieć przed startem

Uruchomienie jest inwazyjne i celowo o tym głośno mówi.

Aplikacja startuje z otwartym portem zdalnego debugowania i świeżym, izolowanym profilem w %TEMP% - nie działa na twoim prawdziwym profilu i nie jest tą kopią, którą masz już w zasobniku. Profil jest kasowany po zakończeniu sesji, a jeśli skasować się nie dał, podsumowanie sesji mówi o tym wprost, zamiast po cichu go zostawiać.

Przekazanie własnego --user-data-dir albo --remote-debugging-port jest odmawiane, a nie nadpisywane, bo oczywisty wypadek - wycelowanie sesji w twój prawdziwy profil przeglądarki - nie jest wart dopuszczenia.

Strefa czasowa sesji idzie za hostem.

W tym trybie fałszowana jest chwila, ale nie funkcje czasu lokalnego, więc niezależna strefa czasowa jest funkcją wyłącznie trybu natywnego. To ograniczenie tej wersji, a nie decyzja.

Jeszcze jedna rzecz, która zaskakuje: interwał już zaplanowany zachowuje stare tempo, gdy zmienisz prędkość w trakcie sesji. Date.now, new Date() i performance.now reagują natychmiast, działający setInterval nie, a sesja ostrzega o tym, zamiast pozwolić ci wyciągnąć wniosek, że zmiana nic nie dała.

Jak dobrze to jest przetestowane

Eksperymentalnie, a to słowo ma tu zdefiniowane znaczenie: działa na celach, na których było budowane, nie było testowane szeroko, a źródłem prawdy jest wbudowany audyt, a nie ta strona. Zostało zweryfikowane ręcznie, a nie przez zautomatyzowany zestaw, na prawdziwej aplikacji Electron na 64-bitowym Windows.

Pokrycie jest raportowane per kontekst JavaScriptu, a nie per proces, bo to jest tutaj istniejąca jednostka. Aplikacja stojąca bezczynnie i nigdy niepytająca o czas uczciwie daje werdykt nieokreślony - nie czytała zegara, więc nie da się twierdzić niczego o tym, czy zobaczyłaby właściwy. Co znaczą werdykty →

Wypróbuj