Make starting of application modular and unit testable (#5324)

* Introduce injection token as competition for injectable setup to have better control of timing of different setups

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Switch to using competition to setup app paths

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Switch to using competition for setupping IPC channel listeners

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Stop running setups in unit tests without need

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Switch to running injection token based setups over legacy DI setups in unit tests

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate naming for running setups

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Switch to running injection token based setups over legacy DI setups in application roots

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Adapt to typing changes in injectable

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Update injectable

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce concept of runnable as a way to delegate runs to Open Closed Principle compliant runnables

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Deprecate vars in favor of injectables

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate loading of extensions to injectable instead of setup-code

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Flag injectable causing side effects

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Adapt injectable to auto register using a plugin instead of internal feature that no longer exists

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Simplify late registrations

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce tokens for runnables of specific application events for Open Closed Principle

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate setup events to more granular timeslots

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for catalog syncing using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for application menu using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for electron application name using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for immer using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for MobX strictness using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for application proxy using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for system certifications using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for system shutdown using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for main window visibility using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for application quit using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for after application is ready using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for application tray using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for deep linking using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Reimplement setup for root frame using runnables instead of non-OCP in index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove multiple usages of shared global state

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove recently reimplemented stuff from index.ts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract implementation for intent over technical phenomena (here electron events)

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Consolidate naming of event timing window

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Move directories for timing windows among peers

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Consolidate event naming

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Introduce function to remove duplication from things that are startable and stoppable

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Consolidate implementation of something startable and stoppable

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Consolidate naming

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Consolidate more startables and stoppables

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Introduce abstractions for electron application events

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce more abstractions for electron

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Abstract even more Electron specifics to make them overridable in unit tests

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Override dependency for causing side effects

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make running of many delegatees able to be hierarchical

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Bump async-fn

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Make startable-stoppable also restartable

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Extract "isIntegrationTesting" as dependency for having a side-effect

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Make temporal dependency apparent

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate all setupping related to single feature to same runnable

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Abstract command line arguments to make them overridable in tests

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Deprecate some globals

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce injectable for __static directory to eliminate side effect on import

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract responsibilities from electron

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract async initialization from sync-injectable

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Accumulate more global overrides

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make lifecycle of injectable store work as expected

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Extract responsibilities to delegatees

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Rename delegate for accuracy

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Rename another delegate for accuracy

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Rename yet another delegate for accuracy

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Add new global overrides

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Consolidate variable naming

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Rename injectable for accuracy

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Make difference between soft and hard quit of application apparent

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Make quit triggered by auto update manifest as hard quit instead of just soft quit

Soft quit is the stand-by quit where only the renderer quits. Hard quit is the full quit of also main.

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Introduce abstraction for publishing and subscribing between processes

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Revert "Introduce abstraction for publishing and subscribing between processes"

This reverts commit 46f2d5a5f28bddcf5ffe124b1c590b19e7b9a15f.

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Adapt code and unit tests to recent changes in application setup and event handling

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Group overrides by category

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Abstract event of application activation from electron

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Abstract event of device shutdown from electron

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate runnables related to Electron

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Make startableStoppables have ID for better error logging

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Make navigating to Helm Charts not blow up

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Add general override for behavioural tests

Co-authored-by: Janne Savolainen <janne.savolainen@live.fi>

Signed-off-by: Iku-turso <mikko.aspiala@gmail.com>

* Update snapshot

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Adapt vars after merge

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix code style

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix error about multiple states for React Router being present at once

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Kludge around test setup which is difficult because of circular dependencies

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix setupping of sentry after rebase

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Ensure that LensProxy is setupped before starting the main window

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove redundant import

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate to use injectable instead of var for static file directory

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Deprecate another var with injectable

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Tweak timing of runnables

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract tray icon path as injectable

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make splash screen not blow up by providing compile-time environment variables

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce a way to run many synchronous runnables

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make runnables for before application is ready synchronous as this is technical limitation of Electron

See: https://github.com/electron/electron/issues/21370

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Kill dead code

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix application quit by calling Electron's event.preventDefault in a very specific way and preventing async

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix missing injectable postfix in file name

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Tweak timeslot of a runnable

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make it possible to not stop something that was never started

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make quit of hidden Lens not blow up

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract responsibilities of WindowManager as injectables

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate code for application window

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Simplify directory structures for runnables

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Flag injectable causing side-effect

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract helpers for testing promises to test-utils

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make script for running unit tests support targeting

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make startable stoppable support async starting

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Introduce reactive way to get theme from operating system

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Update yarn.lock after rebase

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix code style

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove code-style changes that are not welcome

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Switch to using dependency over using explicit side-effect

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Simplify naming

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Kill dead code

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Add empty mocks comply to lint

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove global state

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract responsibility of setting dependencies to own injectable

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Flag injectable causing side effects

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract responsibility to injectable

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Tweak typing

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Tweak naming of runnables

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Revert code-style changes

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Tweak types

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Extract injectable with side-effect for exact overriding

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Tweak name of timeslot to make it more apparent that new content for application load can be added

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Tweak name of the splash window for loading to avoid confusion with similarly named phenomenon

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Tweak comment

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Rename injectable for brevity

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Consolidate LensProxy related stuff to directory

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Rename injectables for accuracy

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Remove usage of extension for injectable for being YAGNI

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Prevent restarting a startableStoppable when already being started

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Rename callback in ApplicationBuilder for accuracy

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix conflicts after rebase

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix merge conflicts

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Revert switch to react-router-dom by installing history as dependency

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make test more strict

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Make runnable give friendly error about incorrect hierarchy for easier debugging

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix code-style

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix code style

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Switch to using dependency instead of legacy-di

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

* Fix timing of injects

Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com>

Signed-off-by: Janne Savolainen <janne.savolainen@live.fi>

Co-authored-by: Iku-turso <mikko.aspiala@gmail.com>
This commit is contained in:
Janne SavolainenandMikko Aspiala authored and GitHub committed 2022-05-18 16:18:02 +03:00
1 parent b57d48e39e
commit 54b92aa9b6
302 files changed
+6388 -2025

No files matched your search

@@ -5,13 +5,19 @@
import { getInjectable } from "@ogre-tools/injectable";
import { apiBase } from "../../common/k8s-api";
import { onApiError } from "./on-api-error";
import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token";
const setupOnApiErrorListenersInjectable = getInjectable({
id: "setup-on-api-error-listeners",
setup: () => {
apiBase?.onError.addListener(onApiError);
},
instantiate: () => undefined,
instantiate: () => ({
run: () => {
apiBase?.onError.addListener(onApiError);
},
}),
injectionToken: beforeFrameStartsInjectionToken,
causesSideEffects: true,
});
export default setupOnApiErrorListenersInjectable;
@@ -1,28 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { AppPaths } from "../../common/app-paths/app-path-injection-token";
import { appPathsInjectionToken, appPathsIpcChannel } from "../../common/app-paths/app-path-injection-token";
import getValueFromRegisteredChannelInjectable from "./get-value-from-registered-channel/get-value-from-registered-channel.injectable";
let syncAppPaths: AppPaths;
const appPathsInjectable = getInjectable({
id: "app-paths",
setup: async (di) => {
const getValueFromRegisteredChannel = await di.inject(
getValueFromRegisteredChannelInjectable,
);
syncAppPaths = await getValueFromRegisteredChannel(appPathsIpcChannel);
},
instantiate: () => syncAppPaths,
injectionToken: appPathsInjectionToken,
});
export default appPathsInjectable;
@@ -0,0 +1,31 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { appPathsIpcChannel } from "../../common/app-paths/app-path-injection-token";
import getValueFromRegisteredChannelInjectable from "./get-value-from-registered-channel/get-value-from-registered-channel.injectable";
import appPathsStateInjectable from "../../common/app-paths/app-paths-state.injectable";
import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token";
const setupAppPathsInjectable = getInjectable({
id: "setup-app-paths",
instantiate: (di) => ({
run: async () => {
const getValueFromRegisteredChannel = di.inject(
getValueFromRegisteredChannelInjectable,
);
const syncAppPaths = await getValueFromRegisteredChannel(appPathsIpcChannel);
const appPathsState = di.inject(appPathsStateInjectable);
appPathsState.set(syncAppPaths);
},
}),
injectionToken: beforeFrameStartsInjectionToken,
});
export default setupAppPathsInjectable;
@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../../common/runnable/run-many-for";
export const beforeFrameStartsInjectionToken = getInjectionToken<Runnable>({
id: "before-frame-starts",
});
+9 -3
View File
@@ -46,6 +46,8 @@ import { init } from "@sentry/electron/renderer";
import kubernetesClusterCategoryInjectable from "../common/catalog/categories/kubernetes-cluster.injectable";
import autoRegistrationInjectable from "../common/k8s-api/api-manager/auto-registration.injectable";
import assert from "assert";
import { beforeFrameStartsInjectionToken } from "./before-frame-starts/before-frame-starts-injection-token";
import { runManyFor } from "../common/runnable/run-many-for";
configurePackages(); // global packages
registerCustomThemes(); // monaco editor themes
@@ -66,7 +68,9 @@ export async function bootstrap(di: DiContainer) {
initializeSentryReporting(init);
}
await di.runSetups();
const beforeFrameStarts = runManyFor(di)(beforeFrameStartsInjectionToken);
await beforeFrameStarts();
// TODO: Consolidate import time side-effect to setup time
bindEvents();
@@ -136,7 +140,7 @@ export async function bootstrap(di: DiContainer) {
await clusterStore.loadInitialOnRenderer();
// HotbarStore depends on: ClusterStore
di.inject(hotbarStoreInjectable);
di.inject(hotbarStoreInjectable).load();
// ThemeStore depends on: UserStore
// TODO: Remove temporal dependencies
@@ -174,9 +178,11 @@ export async function bootstrap(di: DiContainer) {
});
}
const history = di.inject(historyInjectable);
render(
<DiContextProvider value={{ di }}>
<Router history={di.inject(historyInjectable)}>
<Router history={history}>
{DefaultProps(App)}
</Router>
</DiContextProvider>,
@@ -28,6 +28,7 @@ import appEventBusInjectable from "../../../common/app-event-bus/app-event-bus.i
import { computed } from "mobx";
import ipcRendererInjectable from "../../app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
import { UserStore } from "../../../common/user-store";
import broadcastMessageInjectable from "../../../common/ipc/broadcast-message.injectable";
mockWindow();
jest.mock("electron", () => ({
@@ -88,15 +89,16 @@ describe("<Catalog />", () => {
let catalogEntityItem: MockCatalogEntity;
let render: DiRender;
beforeEach(async () => {
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
di.override(broadcastMessageInjectable, () => async () => {});
di.permitSideEffects(getConfigurationFileModelInjectable);
di.permitSideEffects(appVersionInjectable);
await di.runSetups();
mockFs();
CatalogEntityDetailRegistry.createInstance();
@@ -58,7 +58,7 @@ describe("Extensions", () => {
let extensionInstallationStateStore: ExtensionInstallationStateStore;
let render: DiRender;
beforeEach(async () => {
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
@@ -71,8 +71,6 @@ describe("Extensions", () => {
"some-directory-for-user-data": {},
});
await di.runSetups();
render = renderFor(di);
installFromInput = jest.fn();
@@ -79,6 +79,8 @@ class NonInjectedHelmCharts extends Component<Dependencies> {
render() {
return (
<SiblingsInTabLayout>
<div data-testid="page-for-helm-charts" style={{ display: "none" }}/>
<ItemListLayout
isConfigurable
tableId="helm_charts"
@@ -6,16 +6,15 @@ import { getInjectable } from "@ogre-tools/injectable";
import { asyncComputed } from "@ogre-tools/injectable-react";
import namespaceStoreInjectable from "../+namespaces/store.injectable";
import { listReleases } from "../../../common/k8s-api/endpoints/helm-releases.api";
import clusterFrameContextInjectable from "../../cluster-frame-context/cluster-frame-context.injectable";
const releasesInjectable = getInjectable({
id: "releases",
instantiate: (di) => {
const clusterContext = di.inject(clusterFrameContextInjectable);
const namespaceStore = di.inject(namespaceStoreInjectable);
// TODO: Inject clusterContext directly instead of accessing dependency of a dependency
const clusterContext = namespaceStore.context;
return asyncComputed(async () => {
const contextNamespaces = namespaceStore.contextNamespaces || [];
@@ -44,7 +44,7 @@ const extensionPreferenceItemRegistratorInjectable = getInjectable({
}),
);
injectables.forEach(di.register);
di.register(...injectables);
return;
},
@@ -44,7 +44,7 @@ const extensionTelemetryPreferenceItemRegistratorInjectable = getInjectable({
}),
);
injectables.forEach(di.register);
di.register(...injectables);
return;
},
@@ -17,7 +17,7 @@ import ipcRendererInjectable from "../../../../app-paths/get-value-from-register
describe("ClusterRoleBindingDialog tests", () => {
let render: DiRender;
beforeEach(async () => {
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(storesAndApisCanBeCreatedInjectable, () => true);
@@ -26,8 +26,6 @@ describe("ClusterRoleBindingDialog tests", () => {
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
} as never));
await di.runSetups();
render = renderFor(di);
const store = di.inject(clusterRoleStoreInjectable);
@@ -18,7 +18,7 @@ import ipcRendererInjectable from "../../../../app-paths/get-value-from-register
describe("RoleBindingDialog tests", () => {
let render: DiRender;
beforeEach(async () => {
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(storesAndApisCanBeCreatedInjectable, () => true);
@@ -28,8 +28,6 @@ describe("RoleBindingDialog tests", () => {
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
} as never));
await di.runSetups();
render = renderFor(di);
const store = di.inject(clusterRoleStoreInjectable);
@@ -30,7 +30,7 @@ describe("<Welcome/>", () => {
let di: DiContainer;
let welcomeBannersStub: WelcomeBannerRegistration[];
beforeEach(async () => {
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
render = renderFor(di);
welcomeBannersStub = [];
@@ -37,7 +37,7 @@ const tolerations: Toleration[] =[
describe("<PodTolerations />", () => {
let render: DiRender;
beforeEach(async () => {
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForLensLocalStorageInjectable, () => "some-directory-for-lens-local-storage" );
@@ -3,11 +3,11 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { ClusterFrameHandler } from "./lens-views";
import { ClusterFrameHandler } from "./cluster-frame-handler";
const clusterFramesInjectable = getInjectable({
id: "cluster-frames",
const clusterFrameHandlerInjectable = getInjectable({
id: "cluster-frame-handler",
instantiate: () => new ClusterFrameHandler(),
});
export default clusterFramesInjectable;
export default clusterFrameHandlerInjectable;
@@ -9,7 +9,7 @@ import type { IComputedValue } from "mobx";
import { computed, makeObservable, reaction } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react";
import { ClusterStatus } from "./cluster-status";
import type { ClusterFrameHandler } from "./lens-views";
import type { ClusterFrameHandler } from "./cluster-frame-handler";
import type { Cluster } from "../../../common/cluster/cluster";
import { ClusterStore } from "../../../common/cluster-store/cluster-store";
import { requestClusterActivation } from "../../ipc";
@@ -17,7 +17,7 @@ import { withInjectables } from "@ogre-tools/injectable-react";
import type { NavigateToCatalog } from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable";
import clusterViewRouteParametersInjectable from "./cluster-view-route-parameters.injectable";
import clusterFramesInjectable from "./lens-views.injectable";
import clusterFrameHandlerInjectable from "./cluster-frame-handler.injectable";
import type { CatalogEntityRegistry } from "../../api/catalog/entity/registry";
import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable";
@@ -107,7 +107,7 @@ export const ClusterView = withInjectables<Dependencies>(NonInjectedClusterView,
getProps: (di) => ({
clusterId: di.inject(clusterViewRouteParametersInjectable).clusterId,
navigateToCatalog: di.inject(navigateToCatalogInjectable),
clusterFrames: di.inject(clusterFramesInjectable),
clusterFrames: di.inject(clusterFrameHandlerInjectable),
entityRegistry: di.inject(catalogEntityRegistryInjectable),
}),
});
@@ -4,23 +4,28 @@
*/
import "@testing-library/jest-dom/extend-expect";
import { KubeConfig } from "@kubernetes/client-node";
import { fireEvent, screen } from "@testing-library/react";
import { fireEvent } from "@testing-library/react";
import type { RenderResult } from "@testing-library/react";
import mockFs from "mock-fs";
import React from "react";
import * as selectEvent from "react-select-event";
import type { Cluster } from "../../../../common/cluster/cluster";
import { DeleteClusterDialog } from "../view";
import type { ClusterModel } from "../../../../common/cluster-types";
import { getDisForUnitTesting } from "../../../../test-utils/get-dis-for-unit-testing";
import { createClusterInjectionToken } from "../../../../common/cluster/create-cluster-injection-token";
import createContextHandlerInjectable from "../../../../main/context-handler/create-context-handler.injectable";
import type { DiRender } from "../../test-utils/renderFor";
import { renderFor } from "../../test-utils/renderFor";
import type { OpenDeleteClusterDialog } from "../open.injectable";
import openDeleteClusterDialogInjectable from "../open.injectable";
import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable";
import createKubeconfigManagerInjectable from "../../../../main/kubeconfig-manager/create-kubeconfig-manager.injectable";
import ipcRendererInjectable from "../../../app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
import type { ApplicationBuilder } from "../../test-utils/get-application-builder";
import { getApplicationBuilder } from "../../test-utils/get-application-builder";
import { routeInjectionToken } from "../../../../common/front-end-routing/route-injection-token";
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import { routeSpecificComponentInjectionToken } from "../../../routes/route-specific-component-injection-token";
import { navigateToRouteInjectionToken } from "../../../../common/front-end-routing/navigate-to-route-injection-token";
import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable";
jest.mock("electron", () => ({
app: {
@@ -38,33 +43,29 @@ jest.mock("electron", () => ({
},
}));
const kubeconfig = `
const multiClusterConfig = `
apiVersion: v1
clusters:
- cluster:
server: https://localhost
name: test
name: some-current-context-cluster
- cluster:
server: http://localhost
name: other-cluster
name: some-non-current-context-cluster
contexts:
- context:
cluster: test
user: test
name: test
cluster: some-current-context-cluster
user: some-user
name: some-current-context
- context:
cluster: test
user: test
name: test2
- context:
cluster: other-cluster
user: test
name: other-context
current-context: other-context
cluster: some-non-current-context-cluster
user: some-user
name: some-non-current-context
current-context: some-current-context
kind: Config
preferences: {}
users:
- name: test
- name: some-user
user:
token: kubeconfig-user-q4lm4:xxxyyyy
`;
@@ -74,17 +75,17 @@ apiVersion: v1
clusters:
- cluster:
server: http://localhost
name: other-cluster
name: some-cluster
contexts:
- context:
cluster: other-cluster
user: test
name: other-context
current-context: other-context
cluster: some-cluster
user: some-user
name: some-context
current-context: some-context
kind: Config
preferences: {}
users:
- name: test
- name: some-user
user:
token: kubeconfig-user-q4lm4:xxxyyyy
`;
@@ -92,60 +93,107 @@ users:
let config: KubeConfig;
describe("<DeleteClusterDialog />", () => {
let applicationBuilder: ApplicationBuilder;
let createCluster: (model: ClusterModel) => Cluster;
let openDeleteClusterDialog: OpenDeleteClusterDialog;
let render: DiRender;
beforeEach(async () => {
const { mainDi, rendererDi, runSetups } = getDisForUnitTesting({ doGeneralOverrides: true });
applicationBuilder = getApplicationBuilder();
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
mainDi.override(createContextHandlerInjectable, () => () => undefined as any);
mainDi.override(createKubeconfigManagerInjectable, () => () => undefined as any);
rendererDi.override(hotbarStoreInjectable, () => ({}));
rendererDi.override(storesAndApisCanBeCreatedInjectable, () => true);
});
const { rendererDi } = applicationBuilder.dis;
rendererDi.register(testRouteInjectable, testRouteComponentInjectable);
applicationBuilder.beforeRender(({ rendererDi }) => {
const navigateToRoute = rendererDi.inject(navigateToRouteInjectionToken);
const testRoute = rendererDi.inject(testRouteInjectable);
navigateToRoute(testRoute);
});
render = renderFor(rendererDi);
mainDi.override(createContextHandlerInjectable, () => () => undefined);
mainDi.override(createKubeconfigManagerInjectable, () => () => undefined);
mockFs();
rendererDi.override(storesAndApisCanBeCreatedInjectable, () => true);
rendererDi.override(ipcRendererInjectable, () => ({
on: jest.fn(),
invoke: jest.fn(), // TODO: replace with proper mocking via the IPC bridge
} as never));
await runSetups();
openDeleteClusterDialog = rendererDi.inject(openDeleteClusterDialogInjectable);
createCluster = mainDi.inject(createClusterInjectionToken);
applicationBuilder.beforeRender(({ rendererDi }) => {
openDeleteClusterDialog = rendererDi.inject(openDeleteClusterDialogInjectable);
createCluster = rendererDi.inject(createClusterInjectionToken);
});
});
afterEach(() => {
mockFs.restore();
});
it("shows context switcher when deleting current cluster", async () => {
const mockOpts = {
"temp-kube-config": multiClusterConfig,
};
mockFs(mockOpts);
config = new KubeConfig();
config.loadFromString(multiClusterConfig);
applicationBuilder.beforeRender(({ rendererDi }) => {
const createCluster = rendererDi.inject(createClusterInjectionToken);
const cluster = createCluster({
id: "some-current-context-cluster",
contextName: "some-current-context",
preferences: {
clusterName: "some-current-context-cluster",
},
kubeConfigPath: "./temp-kube-config",
});
openDeleteClusterDialog({ cluster, config });
});
const rendered = await applicationBuilder.render();
const { getByText } = rendered;
const menu = getByText("Select new context...");
expect(menu).toBeInTheDocument();
selectEvent.openMenu(menu);
expect(getByText("some-current-context")).toBeInTheDocument();
expect(getByText("some-non-current-context")).toBeInTheDocument();
});
describe("Kubeconfig with different clusters", () => {
let rendered: RenderResult;
beforeEach(async () => {
const mockOpts = {
"temp-kube-config": kubeconfig,
"temp-kube-config": multiClusterConfig,
};
mockFs(mockOpts);
config = new KubeConfig();
config.loadFromString(kubeconfig);
});
config.loadFromString(multiClusterConfig);
afterEach(() => {
mockFs.restore();
rendered = await applicationBuilder.render();
});
it("renders w/o errors", () => {
const { container } = render(<DeleteClusterDialog />);
expect(container).toBeInstanceOf(HTMLElement);
expect(rendered.container).toBeInstanceOf(HTMLElement);
});
it("shows warning when deleting non-current-context cluster", async () => {
it("shows warning when deleting non-current-context cluster", () => {
const cluster = createCluster({
id: "test",
contextName: "test",
id: "some-non-current-context-cluster",
contextName: "some-non-current-context",
preferences: {
clusterName: "minikube",
},
@@ -154,99 +202,78 @@ describe("<DeleteClusterDialog />", () => {
openDeleteClusterDialog({ cluster, config });
render(<DeleteClusterDialog />);
const message = "The contents of kubeconfig file will be changed!";
expect(await screen.findByText(message)).toBeInstanceOf(HTMLElement);
expect(rendered.getByText(message)).toBeInstanceOf(HTMLElement);
});
it("shows warning when deleting current-context cluster", async () => {
it("shows warning when deleting current-context cluster", () => {
const cluster = createCluster({
id: "other-cluster",
contextName: "other-context",
id: "some-current-context-cluster",
contextName: "some-current-context",
preferences: {
clusterName: "other-cluster",
clusterName: "some-current-context-cluster",
},
kubeConfigPath: "./temp-kube-config",
});
openDeleteClusterDialog({ cluster, config });
render(<DeleteClusterDialog />);
expect(await screen.findByTestId("current-context-warning")).toBeInstanceOf(HTMLElement);
expect(rendered.getByTestId("current-context-warning")).toBeInstanceOf(HTMLElement);
});
it("shows context switcher when deleting current cluster", async () => {
it("shows context switcher after checkbox click", () => {
const cluster = createCluster({
id: "other-cluster",
contextName: "other-context",
id: "some-current-context-cluster",
contextName: "some-current-context",
preferences: {
clusterName: "other-cluster",
clusterName: "some-current-context-cluster",
},
kubeConfigPath: "./temp-kube-config",
});
openDeleteClusterDialog({ cluster, config });
render(<DeleteClusterDialog />);
const menu = await screen.findByText("Select new context...");
expect(menu).toBeInTheDocument();
selectEvent.openMenu(menu);
expect(await screen.findByText("test")).toBeInTheDocument();
expect(await screen.findByText("test2")).toBeInTheDocument();
});
it("shows context switcher after checkbox click", async () => {
const cluster = createCluster({
id: "some-cluster",
contextName: "test",
preferences: {
clusterName: "test",
},
kubeConfigPath: "./temp-kube-config",
});
openDeleteClusterDialog({ cluster, config });
render(<DeleteClusterDialog />);
const link = await screen.findByTestId("context-switch");
const { getByText, getByTestId } = rendered;
const link = getByTestId("context-switch");
expect(link).toBeInstanceOf(HTMLElement);
fireEvent.click(link);
const menu = await screen.findByText("Select new context...");
const menu = getByText("Select new context...");
expect(menu).toBeInTheDocument();
selectEvent.openMenu(menu);
expect(await screen.findByText("test")).toBeInTheDocument();
expect(await screen.findByText("test2")).toBeInTheDocument();
expect(getByText("some-current-context")).toBeInTheDocument();
expect(getByText("some-non-current-context")).toBeInTheDocument();
});
it("shows warning for internal kubeconfig cluster", async () => {
it("given cluster in internal kubeconfig, when deleting cluster outside of current context, shows warning for internal kubeconfig cluster", () => {
const cluster = createCluster({
id: "some-cluster",
contextName: "test",
id: "some-non-current-context-cluster",
contextName: "some-non-current-context",
preferences: {
clusterName: "test",
clusterName: "some-non-current-context-cluster",
},
kubeConfigPath: "./temp-kube-config",
});
const spy = jest.spyOn(cluster, "isInLocalKubeconfig").mockImplementation(() => true);
openDeleteClusterDialog({ cluster, config });
render(<DeleteClusterDialog />);
expect(await screen.findByTestId("internal-kubeconfig-warning")).toBeInstanceOf(HTMLElement);
expect(rendered.getByTestId("internal-kubeconfig-warning")).toBeInstanceOf(HTMLElement);
spy.mockRestore();
});
});
describe("Kubeconfig with single cluster", () => {
let rendered: RenderResult;
beforeEach(async () => {
const mockOpts = {
"temp-kube-config": singleClusterConfig,
@@ -256,26 +283,46 @@ describe("<DeleteClusterDialog />", () => {
config = new KubeConfig();
config.loadFromString(singleClusterConfig);
rendered = await applicationBuilder.render();
});
afterEach(() => {
mockFs.restore();
});
it("shows warning if no other contexts left", async () => {
it("shows warning if no other contexts left", () => {
const cluster = createCluster({
id: "other-cluster",
contextName: "other-context",
id: "some-cluster",
contextName: "some-context",
preferences: {
clusterName: "other-cluster",
clusterName: "some-cluster",
},
kubeConfigPath: "./temp-kube-config",
});
openDeleteClusterDialog({ cluster, config });
render(<DeleteClusterDialog />);
expect(await screen.findByTestId("no-more-contexts-warning")).toBeInstanceOf(HTMLElement);
expect(rendered.getByTestId("no-more-contexts-warning")).toBeInstanceOf(HTMLElement);
});
});
});
const testRouteInjectable = getInjectable({
id: "some-test-route",
instantiate: () => ({
path: "/some-test-path",
clusterFrame: false,
isEnabled: computed(() => true),
}),
injectionToken: routeInjectionToken,
});
const testRouteComponentInjectable = getInjectable({
id: "some-test-component",
instantiate: (di) => ({
route: di.inject(testRouteInjectable),
Component: () => <DeleteClusterDialog />,
}),
injectionToken: routeSpecificComponentInjectionToken,
});
@@ -22,7 +22,7 @@ const initialTabs: DockTab[] = [
describe("DockStore", () => {
let dockStore: DockStore;
beforeEach(async () => {
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(hostedClusterIdInjectable, () => "some-cluster-id");
@@ -91,8 +91,6 @@ describe("<DockTabs />", () => {
di.permitSideEffects(getConfigurationFileModelInjectable);
di.permitSideEffects(appVersionInjectable);
await di.runSetups();
dockStore = di.inject(dockStoreInjectable);
await dockStore.whenReady;
@@ -5,20 +5,21 @@
import { getInjectable } from "@ogre-tools/injectable";
import path from "path";
import { hasCorrectExtension } from "./has-correct-extension";
import { staticFilesDirectory } from "../../../../common/vars";
import readFileInjectable from "../../../../common/fs/read-file.injectable";
import readDirInjectable from "../../../../common/fs/read-dir.injectable";
import type { RawTemplates } from "./create-resource-templates.injectable";
import type { GetAbsolutePath } from "../../../../common/path/get-absolute-path.injectable";
import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path.injectable";
import staticFilesDirectoryInjectable from "../../../../common/vars/static-files-directory.injectable";
interface Dependencies {
readDir: (dirPath: string) => Promise<string[]>;
readFile: (filePath: string) => Promise<string>;
getAbsolutePath: GetAbsolutePath;
staticFilesDirectory: string;
}
async function getTemplates({ readDir, readFile, getAbsolutePath }: Dependencies) {
async function getTemplates({ readDir, readFile, getAbsolutePath, staticFilesDirectory }: Dependencies) {
const templatesFolder = getAbsolutePath(staticFilesDirectory, "../templates/create-resource");
/**
@@ -43,6 +44,7 @@ const lensCreateResourceTemplatesInjectable = getInjectable({
readFile: di.inject(readFileInjectable),
readDir: di.inject(readDirInjectable),
getAbsolutePath: di.inject(getAbsolutePathInjectable),
staticFilesDirectory: di.inject(staticFilesDirectoryInjectable),
});
return ["lens", templates];
@@ -127,7 +127,7 @@ const getFewPodsTabData = (tabId: TabId, deps: Partial<LogTabViewModelDependenci
describe("<LogResourceSelector />", () => {
let render: DiRender;
beforeEach(async () => {
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
@@ -142,8 +142,6 @@ describe("<LogResourceSelector />", () => {
render = renderFor(di);
await di.runSetups();
mockFs({
"tmp": {},
});
@@ -60,12 +60,10 @@ const getOnePodViewModel = (tabId: TabId, deps: Partial<LogTabViewModelDependenc
describe("LogSearch tests", () => {
let render: DiRender;
beforeEach(async () => {
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
render = renderFor(di);
await di.runSetups();
});
it("renders w/o errors", () => {
@@ -63,7 +63,7 @@ describe("<HotbarRemoveCommand />", () => {
mockFs.restore();
});
it("renders w/o errors", async () => {
it("renders w/o errors", () => {
di.override(hotbarStoreInjectable, () => ({
hotbars: [mockHotbars["1"]],
getById: (id: string) => mockHotbars[id],
@@ -73,14 +73,12 @@ describe("<HotbarRemoveCommand />", () => {
getDisplayLabel: () => "1: Default",
}) as unknown as HotbarStore);
await di.runSetups();
const { container } = render(<HotbarRemoveCommand />);
expect(container).toBeInstanceOf(HTMLElement);
});
it("calls remove if you click on the entry", async () => {
it("calls remove if you click on the entry", () => {
const removeMock = jest.fn();
di.override(hotbarStoreInjectable, () => ({
@@ -91,8 +89,6 @@ describe("<HotbarRemoveCommand />", () => {
getDisplayLabel: () => "1: Default",
}) as unknown as HotbarStore);
await di.runSetups();
const { getByText } = render(
<>
<HotbarRemoveCommand />
@@ -24,10 +24,11 @@ import type { ThemeStore } from "../../themes/store";
import { MenuActions } from "../menu/menu-actions";
import { MenuItem } from "../menu";
import { Checkbox } from "../checkbox";
import { UserStore } from "../../../common/user-store";
import type { UserStore } from "../../../common/user-store";
import type { ItemListStore } from "./list-layout";
import { withInjectables } from "@ogre-tools/injectable-react";
import themeStoreInjectable from "../../themes/store.injectable";
import userStoreInjectable from "../../../common/user-store/user-store.injectable";
import pageFiltersStoreInjectable from "./page-filters/store.injectable";
import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable";
import openConfirmDialogInjectable from "../confirm-dialog/open.injectable";
@@ -69,6 +70,7 @@ export interface ItemListLayoutContentProps<Item extends ItemObject, PreLoadStor
interface Dependencies {
themeStore: ThemeStore;
userStore: UserStore;
pageFiltersStore: PageFiltersStore;
openConfirmDialog: OpenConfirmDialog;
}
@@ -333,7 +335,7 @@ class NonInjectedItemListLayoutContent<
showColumn({ id: columnId, showWithColumn }: TableCellProps): boolean {
const { tableId, isConfigurable } = this.props;
return !isConfigurable || !tableId || !UserStore.getInstance().isTableColumnHidden(tableId, columnId, showWithColumn);
return !isConfigurable || !tableId || !this.props.userStore.isTableColumnHidden(tableId, columnId, showWithColumn);
}
renderColumnVisibilityMenu() {
@@ -356,7 +358,7 @@ class NonInjectedItemListLayoutContent<
value={this.showColumn(cellProps)}
onChange={(
tableId
? (() => cellProps.id && UserStore.getInstance().toggleTableColumnVisibility(tableId, cellProps.id))
? (() => cellProps.id && this.props.userStore.toggleTableColumnVisibility(tableId, cellProps.id))
: undefined
)}
/>
@@ -373,6 +375,7 @@ export const ItemListLayoutContent = withInjectables<Dependencies, ItemListLayou
getProps: (di, props) => ({
...props,
themeStore: di.inject(themeStoreInjectable),
userStore: di.inject(userStoreInjectable),
pageFiltersStore: di.inject(pageFiltersStoreInjectable),
openConfirmDialog: di.inject(openConfirmDialogInjectable),
}),
@@ -57,7 +57,7 @@ describe("kube-object-menu", () => {
let di: DiContainer;
let render: DiRender;
beforeEach(async () => {
beforeEach(() => {
const MenuItemComponent = () => <li>Some menu item</li>;
const someTestExtension = new SomeTestExtension([
{
@@ -18,7 +18,7 @@ describe("kube-object-status-icon", () => {
let render: DiRender;
let kubeObjectStatusRegistrations: KubeObjectStatusRegistration[];
beforeEach(async () => {
beforeEach(() => {
// TODO: Make mocking of date in unit tests global
global.Date.now = () => new Date("2015-10-21T07:28:00Z").getTime();
@@ -35,8 +35,6 @@ describe("kube-object-status-icon", () => {
di.override(rendererExtensionsInjectable, () =>
computed(() => [someTestExtension]),
);
await di.runSetups();
});
it("given no statuses, when rendered, renders as empty", () => {
@@ -45,7 +45,7 @@ export const TabLayout = observer(({ className, contentClass, tabs = [], childre
key={url}
label={title}
value={url}
active={!!matchPath(currentLocation, { path: routePath, exact })}
active={!!matchPath(currentLocation, { path: routePath, exact })}
/>
))}
</Tabs>
@@ -76,15 +76,13 @@ describe("<TopBar/>", () => {
let di: DiContainer;
let render: DiRender;
beforeEach(async () => {
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
mockFs();
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
await di.runSetups();
render = renderFor(di);
});
@@ -50,6 +50,7 @@ class NonInjectedMenuActions extends React.Component<MenuActionsProps & Dependen
removeConfirmationMessage: "Remove item?",
};
// TODO: Make deterministic
public id = uniqueId("menu_actions_");
@observable isOpen = !!this.props.toolbar;
@@ -30,7 +30,7 @@ describe("<Select />", () => {
let di: DiContainer;
let render: DiRender;
beforeEach(async () => {
beforeEach(() => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
render = renderFor(di);
@@ -39,15 +39,13 @@ describe("<StatusBar />", () => {
let di: DiContainer;
let statusBarItems: IObservableArray<any>;
beforeEach(async () => {
beforeEach(() => {
statusBarItems = observable.array([]);
di = getDiForUnitTesting({ doGeneralOverrides: true });
render = renderFor(di);
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
di.override(rendererExtensionsInjectable, () => computed(() => [new SomeTestExtension(statusBarItems)]));
await di.runSetups();
});
it("renders w/o errors", () => {
@@ -9,7 +9,6 @@ import { extensionRegistratorInjectionToken } from "../../../extensions/extensio
import type { IObservableArray } from "mobx";
import { computed, observable, runInAction } from "mobx";
import { renderFor } from "./renderFor";
import observableHistoryInjectable from "../../navigation/observable-history.injectable";
import React from "react";
import { Router } from "react-router";
import { Observer } from "mobx-react";
@@ -18,7 +17,6 @@ import allowedResourcesInjectable from "../../../common/cluster-store/allowed-re
import type { RenderResult } from "@testing-library/react";
import { fireEvent } from "@testing-library/react";
import type { KubeResource } from "../../../common/rbac";
import directoryForLensLocalStorageInjectable from "../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable";
import { Sidebar } from "../layout/sidebar";
import { getDisForUnitTesting } from "../../../test-utils/get-dis-for-unit-testing";
import type { DiContainer } from "@ogre-tools/injectable";
@@ -34,6 +32,18 @@ import type { MenuItemOpts } from "../../../main/menu/application-menu-items.inj
import applicationMenuItemsInjectable from "../../../main/menu/application-menu-items.injectable";
import type { MenuItem, MenuItemConstructorOptions } from "electron";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import navigateToHelmChartsInjectable from "../../../common/front-end-routing/routes/cluster/helm/charts/navigate-to-helm-charts.injectable";
import hostedClusterInjectable from "../../../common/cluster-store/hosted-cluster.injectable";
import { ClusterFrameContext } from "../../cluster-frame-context/cluster-frame-context";
import type { Cluster } from "../../../common/cluster/cluster";
import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import clusterFrameContextInjectable from "../../cluster-frame-context/cluster-frame-context.injectable";
import startMainApplicationInjectable from "../../../main/start-main-application/start-main-application.injectable";
import startFrameInjectable from "../../start-frame/start-frame.injectable";
import { flushPromises } from "../../../common/test-utils/flush-promises";
import type { NamespaceStore } from "../+namespaces/store";
import namespaceStoreInjectable from "../+namespaces/store.injectable";
import historyInjectable from "../../navigation/history.injectable";
type Callback = (dis: DiContainers) => void | Promise<void>;
@@ -42,12 +52,12 @@ export interface ApplicationBuilder {
setEnvironmentToClusterFrame: () => ApplicationBuilder;
addExtensions: (...extensions: LensRendererExtension[]) => Promise<ApplicationBuilder>;
allowKubeResource: (resourceName: KubeResource) => ApplicationBuilder;
beforeSetups: (callback: Callback) => ApplicationBuilder;
beforeApplicationStart: (callback: Callback) => ApplicationBuilder;
beforeRender: (callback: Callback) => ApplicationBuilder;
render: () => Promise<RenderResult>;
applicationMenu: {
click: (path: string) => void;
click: (path: string) => Promise<void>;
};
preferences: {
@@ -57,6 +67,10 @@ export interface ApplicationBuilder {
click: (id: string) => void;
};
};
helmCharts: {
navigate: () => void;
};
}
interface DiContainers {
@@ -70,13 +84,14 @@ interface Environment {
}
export const getApplicationBuilder = () => {
const { rendererDi, mainDi, runSetups } = getDisForUnitTesting({
const { rendererDi, mainDi } = getDisForUnitTesting({
doGeneralOverrides: true,
});
const dis = { rendererDi, mainDi };
const clusterStoreStub = {
provideInitialFromMain: () => {},
getById: (): null => null,
} as unknown as ClusterStore;
@@ -84,7 +99,7 @@ export const getApplicationBuilder = () => {
rendererDi.override(storesAndApisCanBeCreatedInjectable, () => true);
mainDi.override(clusterStoreInjectable, () => clusterStoreStub);
const beforeSetupsCallbacks: Callback[] = [];
const beforeApplicationStartCallbacks: Callback[] = [];
const beforeRenderCallbacks: Callback[] = [];
const extensionsState = observable.array<LensRendererExtension>();
@@ -130,7 +145,7 @@ export const getApplicationBuilder = () => {
dis,
applicationMenu: {
click: (path: string) => {
click: async (path: string) => {
const applicationMenuItems = mainDi.inject(
applicationMenuItemsInjectable,
);
@@ -160,6 +175,8 @@ export const getApplicationBuilder = () => {
undefined,
{},
);
await flushPromises();
},
},
@@ -199,6 +216,14 @@ export const getApplicationBuilder = () => {
},
},
helmCharts: {
navigate: () => {
const navigateToHelmCharts = rendererDi.inject(navigateToHelmChartsInjectable);
navigateToHelmCharts();
},
},
setEnvironmentToClusterFrame: () => {
environment = environments.clusterFrame;
@@ -208,11 +233,29 @@ export const getApplicationBuilder = () => {
computed(() => new Set([...allowedResourcesState])),
);
rendererDi.override(
directoryForLensLocalStorageInjectable,
() => "/irrelevant",
const clusterStub = {
accessibleNamespaces: [],
} as unknown as Cluster;
const namespaceStoreStub = {
contextNamespaces: [],
} as unknown as NamespaceStore;
const clusterFrameContextFake = new ClusterFrameContext(
clusterStub,
{
namespaceStore: namespaceStoreStub,
},
);
rendererDi.override(namespaceStoreInjectable, () => namespaceStoreStub);
rendererDi.override(hostedClusterInjectable, () => clusterStub);
rendererDi.override(clusterFrameContextInjectable, () => clusterFrameContextFake);
// Todo: get rid of global state.
KubeObjectStore.defaultContext.set(clusterFrameContextFake);
return builder;
},
@@ -254,8 +297,8 @@ export const getApplicationBuilder = () => {
return builder;
},
beforeSetups(callback: (dis: DiContainers) => void) {
beforeSetupsCallbacks.push(callback);
beforeApplicationStart(callback: (dis: DiContainers) => void) {
beforeApplicationStartCallbacks.push(callback);
return builder;
},
@@ -267,14 +310,20 @@ export const getApplicationBuilder = () => {
},
async render() {
for (const callback of beforeSetupsCallbacks) {
for (const callback of beforeApplicationStartCallbacks) {
await callback(dis);
}
await runSetups();
const startMainApplication = mainDi.inject(startMainApplicationInjectable);
await startMainApplication();
const startFrame = rendererDi.inject(startFrameInjectable);
await startFrame();
const render = renderFor(rendererDi);
const history = rendererDi.inject(observableHistoryInjectable);
const history = rendererDi.inject(historyInjectable);
const currentRouteComponent = rendererDi.inject(currentRouteComponentInjectable);
for (const callback of beforeRenderCallbacks) {
@@ -15,12 +15,17 @@ const createClusterInjectable = getInjectable({
instantiate: (di) => {
const dependencies: ClusterDependencies = {
directoryForKubeConfigs: di.inject(directoryForKubeConfigsInjectable),
createKubeconfigManager: () => undefined,
logger: di.inject(loggerInjectable),
// TODO: Dismantle wrong abstraction
// Note: "as never" to get around strictness in unnatural scenario
createKubeconfigManager: () => undefined as never,
createKubectl: () => { throw new Error("Tried to access back-end feature in front-end.");},
createContextHandler: () => undefined,
createContextHandler: () => undefined as never,
createAuthorizationReview: () => { throw new Error("Tried to access back-end feature in front-end."); },
createListNamespaces: () => { throw new Error("Tried to access back-end feature in front-end."); },
logger: di.inject(loggerInjectable),
detectorRegistry: undefined as never,
createVersionDetector: () => { throw new Error("Tried to access back-end feature in front-end."); },
};
return (model) => new Cluster(dependencies, model);
+11 -14
View File
@@ -4,25 +4,22 @@
*/
import { createContainer } from "@ogre-tools/injectable";
import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration";
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
export const getDi = () => {
const di = createContainer(
getRequireContextForRendererCode,
getRequireContextForCommonExtensionCode,
getRequireContextForCommonCode,
);
const di = createContainer();
autoRegister({
di,
requireContexts: [
require.context("./", true, /\.injectable\.(ts|tsx)$/),
require.context("../common", true, /\.injectable\.(ts|tsx)$/),
require.context("../extensions", true, /\.injectable\.(ts|tsx)$/),
],
});
setLegacyGlobalDiForExtensionApi(di, Environments.renderer);
return di;
};
const getRequireContextForRendererCode = () =>
require.context("./", true, /\.injectable\.(ts|tsx)$/);
const getRequireContextForCommonCode = () =>
require.context("../common", true, /\.injectable\.(ts|tsx)$/);
const getRequireContextForCommonExtensionCode = () =>
require.context("../extensions", true, /\.injectable\.(ts|tsx)$/);
+38 -3
View File
@@ -4,7 +4,7 @@
*/
import glob from "glob";
import { memoize } from "lodash/fp";
import { memoize, noop } from "lodash/fp";
import { createContainer } from "@ogre-tools/injectable";
import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import getValueFromRegisteredChannelInjectable from "./app-paths/get-value-from-registered-channel/get-value-from-registered-channel.injectable";
@@ -34,12 +34,21 @@ import terminalSpawningPoolInjectable from "./components/dock/terminal/terminal-
import hostedClusterIdInjectable from "../common/cluster-store/hosted-cluster-id.injectable";
import type { GetDiForUnitTestingOptions } from "../test-utils/get-dis-for-unit-testing";
import historyInjectable from "./navigation/history.injectable";
import { noop } from "./utils";
import { ApiManager } from "../common/k8s-api/api-manager";
import lensResourcesDirInjectable from "../common/vars/lens-resources-dir.injectable";
import broadcastMessageInjectable from "../common/ipc/broadcast-message.injectable";
import apiManagerInjectable from "../common/k8s-api/api-manager/manager.injectable";
import ipcRendererInjectable
from "./app-paths/get-value-from-registered-channel/ipc-renderer/ipc-renderer.injectable";
import type { IpcRenderer } from "electron";
import setupOnApiErrorListenersInjectable from "./api/setup-on-api-errors.injectable";
import { observable } from "mobx";
export const getDiForUnitTesting = (opts: GetDiForUnitTestingOptions = {}) => {
const {
doGeneralOverrides = false,
} = opts;
const di = createContainer();
setLegacyGlobalDiForExtensionApi(di, Environments.renderer);
@@ -68,6 +77,17 @@ export const getDiForUnitTesting = (opts: GetDiForUnitTestingOptions = {}) => {
di.override(historyInjectable, () => createMemoryHistory());
di.override(lensResourcesDirInjectable, () => "/irrelevant");
di.override(ipcRendererInjectable, () => ({
invoke: () => {},
on: () => {},
}) as unknown as IpcRenderer);
di.override(broadcastMessageInjectable, () => () => {
throw new Error("Tried to broadcast message over IPC without explicit override.");
});
// eslint-disable-next-line unused-imports/no-unused-vars-ts
di.override(extensionsStoreInjectable, () => ({ isEnabled: ({ id, isBundled }) => false }) as ExtensionsStore);
@@ -77,7 +97,22 @@ export const getDiForUnitTesting = (opts: GetDiForUnitTestingOptions = {}) => {
// eslint-disable-next-line unused-imports/no-unused-vars-ts
di.override(clusterStoreInjectable, () => ({ getById: (id): Cluster => ({}) as Cluster }) as ClusterStore);
di.override(userStoreInjectable, () => ({}) as UserStore);
di.override(setupOnApiErrorListenersInjectable, () => ({ run: () => {} }));
di.override(
userStoreInjectable,
() =>
({
isTableColumnHidden: () => false,
extensionRegistryUrl: { customUrl: "some-custom-url" },
syncKubeconfigEntries: observable.map(),
terminalConfig: { fontSize: 42 },
editorConfiguration: { minimap: {}, tabSize: 42, fontSize: 42 },
} as unknown as UserStore),
);
di.override(apiManagerInjectable, () => new ApiManager());
di.override(getValueFromRegisteredChannelInjectable, () => () => Promise.resolve(undefined as never));
di.override(registerIpcChannelListenerInjectable, () => () => undefined);
@@ -3,25 +3,26 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { noop } from "lodash/fp";
import { ipcChannelListenerInjectionToken } from "./ipc-channel-listener-injection-token";
import registerIpcChannelListenerInjectable
from "../app-paths/get-value-from-registered-channel/register-ipc-channel-listener.injectable";
import registerIpcChannelListenerInjectable from "../app-paths/get-value-from-registered-channel/register-ipc-channel-listener.injectable";
import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token";
const registerIpcChannelListenersInjectable = getInjectable({
id: "register-ipc-channel-listeners",
setup: async di => {
const registerIpcChannelListener = await di.inject(registerIpcChannelListenerInjectable);
instantiate: di => ({
run: async () => {
const registerIpcChannelListener = di.inject(registerIpcChannelListenerInjectable);
const listeners = await di.injectMany(ipcChannelListenerInjectionToken);
const listeners = di.injectMany(ipcChannelListenerInjectionToken);
listeners.forEach(listener => {
registerIpcChannelListener(listener);
});
},
listeners.forEach(listener => {
registerIpcChannelListener(listener);
});
},
}),
instantiate: () => noop,
injectionToken: beforeFrameStartsInjectionToken,
});
export default registerIpcChannelListenersInjectable;
@@ -4,10 +4,11 @@
*/
import { getInjectable } from "@ogre-tools/injectable";
import { createBrowserHistory } from "history";
import type { History } from "history";
const historyInjectable = getInjectable({
id: "history",
instantiate: () => createBrowserHistory(),
instantiate: (): History => createBrowserHistory(),
});
export default historyInjectable;
@@ -10,6 +10,7 @@ import historyInjectable from "./history.injectable";
const observableHistoryInjectable = getInjectable({
id: "observable-history",
instantiate: (di) => {
const history = di.inject(historyInjectable);
const logger = di.inject(loggerInjectable);
@@ -2,7 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DiContainerForInstantiate } from "@ogre-tools/injectable";
import type { DiContainerForInjection } from "@ogre-tools/injectable";
import { getInjectable } from "@ogre-tools/injectable";
import type { LensRendererExtension } from "../../extensions/lens-renderer-extension";
@@ -35,7 +35,7 @@ const extensionRouteRegistratorInjectable = getInjectable({
...extension.clusterPages.map(toRouteInjectable(true)),
].flat();
routeInjectables.forEach(di.register as never);
di.register(...routeInjectables);
};
},
@@ -46,7 +46,7 @@ export default extensionRouteRegistratorInjectable;
const toRouteInjectableFor =
(
di: DiContainerForInstantiate,
di: DiContainerForInjection,
extension: LensRendererExtension,
extensionInstallationCount: number,
) =>
@@ -7,14 +7,15 @@ import observableHistoryInjectable from "../navigation/observable-history.inject
import { runInAction } from "mobx";
import type { NavigateToUrl } from "../../common/front-end-routing/navigate-to-url-injection-token";
import { navigateToUrlInjectionToken } from "../../common/front-end-routing/navigate-to-url-injection-token";
import { broadcastMessage } from "../../common/ipc";
import { IpcRendererNavigationEvents } from "../navigation/events";
import broadcastMessageInjectable from "../../common/ipc/broadcast-message.injectable";
const navigateToUrlInjectable = getInjectable({
id: "navigate-to-url",
instantiate: (di): NavigateToUrl => {
const observableHistory = di.inject(observableHistoryInjectable);
const broadcastMessage = di.inject(broadcastMessageInjectable);
return (url, options = {}): void => {
if (options.forceRootFrame) {
@@ -28,13 +28,11 @@ const logs = [
describe("search store tests", () => {
let searchStore: SearchStore;
beforeEach(async () => {
beforeEach(() => {
const di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
await di.runSetups();
searchStore = di.inject(searchStoreInjectable);
});
@@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { runManyFor } from "../../common/runnable/run-many-for";
import { beforeFrameStartsInjectionToken } from "../before-frame-starts/before-frame-starts-injection-token";
const startFrameInjectable = getInjectable({
id: "start-frame",
// TODO: Consolidate contents of bootstrap.tsx here
instantiate: (di) => async () => {
const beforeFrameStarts = runManyFor(di)(beforeFrameStartsInjectionToken);
await beforeFrameStarts();
},
});
export default startFrameInjectable;