mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Introduce way to install update directly from tray. Make application updater unit testable... (#5433)
* Extract product name as injectable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make tray items comply with Open Closed Principle Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Replace duplicated overrides with global Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add behaviour for navigating to preferences using tray Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce a tray item for updating application Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak naming Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak more naming Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant indirection Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak more naming Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce injectable for package.json being side-effect Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Relocate file to directory containing feature Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using injectable for limiting side effect Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add missing injection token for implementation of tray item Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove resetting state for update is ready to be installed for being unclear Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Kill dead code Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make label of tray item reactive Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract updating is enabled to separate injectable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for tray Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Expand scope of behaviour for updating using tray also contain checking for updates Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove dead code 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> * Implement checking of updates from multiple update channels Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Start installing updates automatically when quitting application Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Show application window when checking of updates has happened Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Show notifications and dialog for downloading update Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Implement naive notifications for version updates Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Implement checking of Electron specific updates as responsibility Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Implement downloading of Electron specific updates as responsibility Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce competition for channel abstraction Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant global override Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix typing after enabling strict mode Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce abstraction for a state that is shared between environments Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract states of application update to be usable from all environments Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Handle failing download of update Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make code for window visibility actually work Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate code for sending messages between processes to a window Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Split bloated dependency in smaller pieces Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make state of download progress accessible from all environments Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Rename files for accuracy Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move channel abstraction to more global directory Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Enhance typing of channels and sync-box Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate channel abstraction types Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Update asyncFn to support strict mode Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix snapshot after rebase Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add missing global override Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce injection token for channels to allow injecting all of them at once Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add notifications about change in update status Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Rename property for accuracy Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak code style Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make notifications unit testable in behaviours Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add implementation for asking boolean over processes Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reorganize responsibilities for checking updates Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Reorganize tests for installing update under separate scenarios Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make stuff happening when root frame is rendered unit testable Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce periodical check for updates Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Allow downgrading app versions Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using competition for checking of updates in application menu Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Kill dead code Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make test less prone to fail for wrong reason Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant boilerplate Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make tests for specific migrations less prone to failing for wrong reason Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move shared stuff under common Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using single source of truth for selected update channel Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Extract tests for installing update from different update channels to separate scenario Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add missing global override Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Switch to using release channel of installed application version as default value for selected update channel Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate usage of channel abstraction to same implementation Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make Channel abstraction support return values Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix direct calling of runnables Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Synchronize initial values of sync boxes when window starts Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add missing global override Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak message of question from user Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Consolidate names of directories Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Add TODO Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove unimplemented scenario from test Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Simplify test Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Improve name of test Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant overrides Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix code style Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make Animate deterministic in unit tests Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Simplify naming Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify more naming Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify even more naming Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify more and more naming Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Add todo for cleaning unacceptable code encountered Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Improve name of behaviour Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make unit test more strict Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Enhance name of behaviour Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce dependency to get random IDs Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make asking of boolean value from user not require explicit ID for question Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Simplify code for asking of boolean value from user Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make setting of initial state for sync boxes not trigger irrelevant messaging to main Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make a channel have default type for sent and returned message Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce higher order function to log errors in decorated functions Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Export type for error logging Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak test name Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce higher order function for suppressing errors Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Relocate some explicit error handlings to proper level of abstraction Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make higher order function for logging errors support asynchronous rejecting with non error instance Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make overridden version of application exactly the one required by unit test Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Mark injectable causing side effects Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Revert not required changes Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make code for asserting a promise more strict Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Make dependencies readonly Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove duplication for disposers Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Implement initial values for sync-boxes Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Separate concept of message and request channels Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Introduce tests for requesting from channel in renderer Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Implement requesting from renderer in main Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Revert "Implement requesting from renderer in main" This reverts commit d3e7899d7900516f3dbfacdb317a453202318305. Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak typing of request channel listeners to get rid of unexpected undefined Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove unused variable Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Tweak timing of sentry setup Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Require messages for MessageChannels be JsonValues for serialization Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Require requests and responses for RequestChannels be JsonValues for serialization Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make different MessageChannels not require explicit "extends JsonObject" Note: Non-escaped lint breaks type here for forcing interface over type. Reasonable effort brought no understanding for what is the relevant difference between the two. Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make a primitive argument an object for readability Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make typing of higher order function for error suppression not lie Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Serialize messages in channels to make IPC not blow up Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Introduce a way to make intentional orphan promises uniform, controllable and deliberate Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Make downloading of update and what follows more deliberate as orphan promise Co-authored-by: Janne Savolainen <janne.savolainen@live.fi> Signed-off-by: Iku-turso <mikko.aspiala@gmail.com> * Move utility function under directory Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Move another utility function under directory Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix incorrect name of file Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Remove redundant code 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> * Round percentage of update download progress in tray Co-authored-by: Mikko Aspiala <mikko.aspiala@gmail.com> Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix rebase conflicts Signed-off-by: Janne Savolainen <janne.savolainen@live.fi> * Fix CheckForUpdate type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> Co-authored-by: Iku-turso <mikko.aspiala@gmail.com> Co-authored-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
e1c1e00a2b
commit
b414f9e06d
@ -281,7 +281,7 @@
|
|||||||
"ws": "^8.5.0"
|
"ws": "^8.5.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@async-fn/jest": "1.6.0",
|
"@async-fn/jest": "1.6.1",
|
||||||
"@material-ui/core": "^4.12.3",
|
"@material-ui/core": "^4.12.3",
|
||||||
"@material-ui/icons": "^4.11.2",
|
"@material-ui/icons": "^4.11.2",
|
||||||
"@material-ui/lab": "^4.0.0-alpha.60",
|
"@material-ui/lab": "^4.0.0-alpha.60",
|
||||||
|
|||||||
@ -1,11 +1,20 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`extension special characters in page registrations renders 1`] = `<div />`;
|
exports[`extension special characters in page registrations renders 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`extension special characters in page registrations when navigating to route with ID having special characters renders 1`] = `
|
exports[`extension special characters in page registrations when navigating to route with ID having special characters renders 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
Some page
|
Some page
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,12 +1,21 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`navigate to extension page renders 1`] = `<div />`;
|
exports[`navigate to extension page renders 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`navigate to extension page when extension navigates to child route renders 1`] = `
|
exports[`navigate to extension page when extension navigates to child route renders 1`] = `
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
Child page
|
Child page
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -31,6 +40,9 @@ exports[`navigate to extension page when extension navigates to route with param
|
|||||||
Some button
|
Some button
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -55,6 +67,9 @@ exports[`navigate to extension page when extension navigates to route without pa
|
|||||||
Some button
|
Some button
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -79,5 +94,8 @@ exports[`navigate to extension page when extension navigates to route without pa
|
|||||||
Some button
|
Some button
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -8,6 +8,9 @@ exports[`navigating between routes given route with optional path parameters whe
|
|||||||
"someOtherParameter": "some-other-value"
|
"someOtherParameter": "some-other-value"
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -16,5 +19,8 @@ exports[`navigating between routes given route without path parameters when navi
|
|||||||
<div>
|
<div>
|
||||||
Some component
|
Some component
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`add-cluster - navigation using application menu renders 1`] = `<div />`;
|
exports[`add-cluster - navigation using application menu renders 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`add-cluster - navigation using application menu when navigating to add cluster using application menu renders 1`] = `
|
exports[`add-cluster - navigation using application menu when navigating to add cluster using application menu renders 1`] = `
|
||||||
<div>
|
<div>
|
||||||
@ -85,5 +91,8 @@ exports[`add-cluster - navigation using application menu when navigating to add
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -6,16 +6,17 @@
|
|||||||
import type { RenderResult } from "@testing-library/react";
|
import type { RenderResult } from "@testing-library/react";
|
||||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
// TODO: Make components free of side effects by making them deterministic
|
// TODO: Make components free of side effects by making them deterministic
|
||||||
jest.mock("../../renderer/components/tooltip/tooltip", () => ({
|
jest.mock("../../renderer/components/tooltip/tooltip", () => ({
|
||||||
Tooltip: () => null,
|
Tooltip: () => null,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
|
jest.mock("../../renderer/components/tooltip/withTooltip", () => ({
|
||||||
withTooltip: (Target: any) => ({ tooltip, tooltipOverrideDisabled, ...props }: any) => <Target {...props} />,
|
withTooltip: (Target: any) => ({ tooltip, tooltipOverrideDisabled, ...props }: any) => <Target {...props} />,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock("../../renderer/components/monaco-editor/monaco-editor", () => ({
|
jest.mock("../../renderer/components/monaco-editor/monaco-editor", () => ({
|
||||||
MonacoEditor: () => null,
|
MonacoEditor: () => null,
|
||||||
}));
|
}));
|
||||||
@ -25,9 +26,7 @@ describe("add-cluster - navigation using application menu", () => {
|
|||||||
let rendered: RenderResult;
|
let rendered: RenderResult;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
applicationBuilder = getApplicationBuilder().beforeApplicationStart(({ mainDi }) => {
|
applicationBuilder = getApplicationBuilder();
|
||||||
mainDi.override(isAutoUpdateEnabledInjectable, () => () => false);
|
|
||||||
});
|
|
||||||
|
|
||||||
rendered = await applicationBuilder.render();
|
rendered = await applicationBuilder.render();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -0,0 +1,536 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`installing update using tray when started renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update using tray when started when user checks for updates using tray renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
Checking for updates...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-notification_13"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update using tray when started when user checks for updates using tray when new update is discovered renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
Checking for updates...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-notification_96"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
Download for version some-version started...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-notification_99"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update using tray when started when user checks for updates using tray when new update is discovered when download fails renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
Checking for updates...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-notification_149"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
Download for version some-version started...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-notification_152"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
Download of update failed
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-notification_157"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update using tray when started when user checks for updates using tray when new update is discovered when download succeeds renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
Checking for updates...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-notification_215"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
Download for version some-version started...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-notification_218"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="flex column gaps"
|
||||||
|
data-testid="ask-boolean-some-irrelevant-random-id"
|
||||||
|
>
|
||||||
|
<b>
|
||||||
|
Update Available
|
||||||
|
</b>
|
||||||
|
<p>
|
||||||
|
Version some-version of Lens IDE is available and ready to be installed. Would you like to update now?
|
||||||
|
|
||||||
|
Lens should restart automatically, if it doesn't please restart manually. Installed extensions might require updating.
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="flex gaps row align-left box grow"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="Button light"
|
||||||
|
data-testid="ask-boolean-some-irrelevant-random-id-button-yes"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
Yes
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="Button active outlined"
|
||||||
|
data-testid="ask-boolean-some-irrelevant-random-id-button-no"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
No
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-ask-boolean-for-some-irrelevant-random-id"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update using tray when started when user checks for updates using tray when no new update is discovered renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
Checking for updates...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-notification_48"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Animate opacity notification flex info enter"
|
||||||
|
style="--enter-duration: 100ms; --leave-duration: 100ms;"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="info_outline"
|
||||||
|
>
|
||||||
|
info_outline
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="message box grow"
|
||||||
|
>
|
||||||
|
No new updates available
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="box"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon close material interactive focusable"
|
||||||
|
data-testid="close-notification-for-notification_51"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`installing update when started renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update when started when user checks for updates renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update when started when user checks for updates when new update is discovered renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update when started when user checks for updates when new update is discovered when download fails renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update when started when user checks for updates when new update is discovered when download succeeds renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update when started when user checks for updates when new update is discovered when download succeeds when user answers not to install the update renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update when started when user checks for updates when new update is discovered when download succeeds when user answers to install the update renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`installing update when started when user checks for updates when no new update is discovered renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`periodical checking of updates given updater is enabled and configuration exists, when started renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`selection of update stability when started renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
|
||||||
|
import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import type { CheckForPlatformUpdates } from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
|
||||||
|
import checkForPlatformUpdatesInjectable from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
|
||||||
|
import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable";
|
||||||
|
import selectedUpdateChannelInjectable from "../../common/application-update/selected-update-channel/selected-update-channel.injectable";
|
||||||
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
|
import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.injectable";
|
||||||
|
import { updateChannels } from "../../common/application-update/update-channels";
|
||||||
|
|
||||||
|
describe("downgrading version update", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
|
||||||
|
let mainDi: DiContainer;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
checkForPlatformUpdatesMock = asyncFn();
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
checkForPlatformUpdatesInjectable,
|
||||||
|
() => checkForPlatformUpdatesMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
|
||||||
|
mainDi.override(publishIsConfiguredInjectable, () => true);
|
||||||
|
});
|
||||||
|
|
||||||
|
mainDi = applicationBuilder.dis.mainDi;
|
||||||
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
updateChannel: updateChannels.latest,
|
||||||
|
appVersion: "4.0.0-beta",
|
||||||
|
downgradeIsAllowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
updateChannel: updateChannels.beta,
|
||||||
|
appVersion: "4.0.0-beta",
|
||||||
|
downgradeIsAllowed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
updateChannel: updateChannels.beta,
|
||||||
|
appVersion: "4.0.0-beta.1",
|
||||||
|
downgradeIsAllowed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
updateChannel: updateChannels.alpha,
|
||||||
|
appVersion: "4.0.0-beta",
|
||||||
|
downgradeIsAllowed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
updateChannel: updateChannels.alpha,
|
||||||
|
appVersion: "4.0.0-alpha",
|
||||||
|
downgradeIsAllowed: false,
|
||||||
|
},
|
||||||
|
].forEach(({ appVersion, updateChannel, downgradeIsAllowed }) => {
|
||||||
|
it(`given application version "${appVersion}" and update channel "${updateChannel.id}", when checking for updates, can${downgradeIsAllowed ? "": "not"} downgrade`, async () => {
|
||||||
|
mainDi.override(appVersionInjectable, () => appVersion);
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
|
||||||
|
const selectedUpdateChannel = mainDi.inject(selectedUpdateChannelInjectable);
|
||||||
|
|
||||||
|
selectedUpdateChannel.setValue(updateChannel.id);
|
||||||
|
|
||||||
|
const processCheckingForUpdates = mainDi.inject(processCheckingForUpdatesInjectable);
|
||||||
|
|
||||||
|
processCheckingForUpdates();
|
||||||
|
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(expect.any(Object), { allowDowngrade: downgradeIsAllowed });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,235 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
|
||||||
|
import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable";
|
||||||
|
import type { CheckForPlatformUpdates } from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
|
||||||
|
import checkForPlatformUpdatesInjectable from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import type { DownloadPlatformUpdate } from "../../main/application-update/download-platform-update/download-platform-update.injectable";
|
||||||
|
import downloadPlatformUpdateInjectable from "../../main/application-update/download-platform-update/download-platform-update.injectable";
|
||||||
|
import showApplicationWindowInjectable from "../../main/start-main-application/lens-window/show-application-window.injectable";
|
||||||
|
import progressOfUpdateDownloadInjectable from "../../common/application-update/progress-of-update-download/progress-of-update-download.injectable";
|
||||||
|
|
||||||
|
describe("installing update using tray", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
|
||||||
|
let downloadPlatformUpdateMock: AsyncFnMock<DownloadPlatformUpdate>;
|
||||||
|
let showApplicationWindowMock: jest.Mock;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
checkForPlatformUpdatesMock = asyncFn();
|
||||||
|
downloadPlatformUpdateMock = asyncFn();
|
||||||
|
showApplicationWindowMock = jest.fn();
|
||||||
|
|
||||||
|
mainDi.override(showApplicationWindowInjectable, () => showApplicationWindowMock);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
checkForPlatformUpdatesInjectable,
|
||||||
|
() => checkForPlatformUpdatesMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
downloadPlatformUpdateInjectable,
|
||||||
|
() => downloadPlatformUpdateMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
|
||||||
|
mainDi.override(publishIsConfiguredInjectable, () => true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when started", () => {
|
||||||
|
let rendered: RenderResult;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user cannot install update yet", () => {
|
||||||
|
expect(applicationBuilder.tray.get("install-update")).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when user checks for updates using tray", () => {
|
||||||
|
let processCheckingForUpdatesPromise: Promise<void>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
processCheckingForUpdatesPromise =
|
||||||
|
applicationBuilder.tray.click("check-for-updates");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show application window yet", () => {
|
||||||
|
expect(showApplicationWindowMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user cannot check for updates again", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("name of tray item for checking updates indicates that checking is happening", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
|
).toBe("Checking for updates...");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user cannot install update yet", () => {
|
||||||
|
expect(applicationBuilder.tray.get("install-update")).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when no new update is discovered", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await checkForPlatformUpdatesMock.resolve({
|
||||||
|
updateWasDiscovered: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await processCheckingForUpdatesPromise;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows application window", () => {
|
||||||
|
expect(showApplicationWindowMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user cannot install update", () => {
|
||||||
|
expect(applicationBuilder.tray.get("install-update")).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user can check for updates again", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("name of tray item for checking updates no longer indicates that checking is happening", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
|
).toBe("Check for updates");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when new update is discovered", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await checkForPlatformUpdatesMock.resolve({
|
||||||
|
updateWasDiscovered: true,
|
||||||
|
version: "some-version",
|
||||||
|
});
|
||||||
|
|
||||||
|
await processCheckingForUpdatesPromise;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows application window", () => {
|
||||||
|
expect(showApplicationWindowMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user cannot check for updates again yet", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
|
||||||
|
).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("name of tray item for checking updates indicates that downloading is happening", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
|
).toBe("Downloading update some-version (0%)...");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when download progresses with decimals, percentage increases as integers", () => {
|
||||||
|
const progressOfUpdateDownload = applicationBuilder.dis.mainDi.inject(
|
||||||
|
progressOfUpdateDownloadInjectable,
|
||||||
|
);
|
||||||
|
|
||||||
|
progressOfUpdateDownload.set({ percentage: 42.424242 });
|
||||||
|
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
|
).toBe("Downloading update some-version (42%)...");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user still cannot install update", () => {
|
||||||
|
expect(applicationBuilder.tray.get("install-update")).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when download fails", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user cannot install update", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("install-update"),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user can check for updates again", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
|
).toBe("Check for updates");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when download succeeds", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user can install update", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("install-update")?.label?.get(),
|
||||||
|
).toBe("Install update some-version");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("user can check for updates again", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.enabled.get(),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("name of tray item for checking updates no longer indicates that downloading is happening", () => {
|
||||||
|
expect(
|
||||||
|
applicationBuilder.tray.get("check-for-updates")?.label?.get(),
|
||||||
|
).toBe("Check for updates");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
225
src/behaviours/application-update/installing-update.test.ts
Normal file
225
src/behaviours/application-update/installing-update.test.ts
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import quitAndInstallUpdateInjectable from "../../main/electron-app/features/quit-and-install-update.injectable";
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
|
||||||
|
import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable";
|
||||||
|
import type { CheckForPlatformUpdates } from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
|
||||||
|
import checkForPlatformUpdatesInjectable from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import type { DownloadPlatformUpdate } from "../../main/application-update/download-platform-update/download-platform-update.injectable";
|
||||||
|
import downloadPlatformUpdateInjectable from "../../main/application-update/download-platform-update/download-platform-update.injectable";
|
||||||
|
import setUpdateOnQuitInjectable from "../../main/electron-app/features/set-update-on-quit.injectable";
|
||||||
|
import type { AskBoolean } from "../../main/ask-boolean/ask-boolean.injectable";
|
||||||
|
import askBooleanInjectable from "../../main/ask-boolean/ask-boolean.injectable";
|
||||||
|
import showInfoNotificationInjectable from "../../renderer/components/notifications/show-info-notification.injectable";
|
||||||
|
import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable";
|
||||||
|
|
||||||
|
describe("installing update", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let quitAndInstallUpdateMock: jest.Mock;
|
||||||
|
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
|
||||||
|
let downloadPlatformUpdateMock: AsyncFnMock<DownloadPlatformUpdate>;
|
||||||
|
let setUpdateOnQuitMock: jest.Mock;
|
||||||
|
let showInfoNotificationMock: jest.Mock;
|
||||||
|
let askBooleanMock: AsyncFnMock<AskBoolean>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
|
||||||
|
quitAndInstallUpdateMock = jest.fn();
|
||||||
|
checkForPlatformUpdatesMock = asyncFn();
|
||||||
|
downloadPlatformUpdateMock = asyncFn();
|
||||||
|
setUpdateOnQuitMock = jest.fn();
|
||||||
|
showInfoNotificationMock = jest.fn(() => () => {});
|
||||||
|
askBooleanMock = asyncFn();
|
||||||
|
|
||||||
|
rendererDi.override(showInfoNotificationInjectable, () => showInfoNotificationMock);
|
||||||
|
|
||||||
|
mainDi.override(askBooleanInjectable, () => askBooleanMock);
|
||||||
|
mainDi.override(setUpdateOnQuitInjectable, () => setUpdateOnQuitMock);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
checkForPlatformUpdatesInjectable,
|
||||||
|
() => checkForPlatformUpdatesMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
downloadPlatformUpdateInjectable,
|
||||||
|
() => downloadPlatformUpdateMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
quitAndInstallUpdateInjectable,
|
||||||
|
() => quitAndInstallUpdateMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
|
||||||
|
mainDi.override(publishIsConfiguredInjectable, () => true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when started", () => {
|
||||||
|
let rendered: RenderResult;
|
||||||
|
let processCheckingForUpdates: () => Promise<void>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
|
||||||
|
processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when user checks for updates", () => {
|
||||||
|
let processCheckingForUpdatesPromise: Promise<void>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
processCheckingForUpdatesPromise = processCheckingForUpdates();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("checks for updates", () => {
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(
|
||||||
|
expect.any(Object),
|
||||||
|
{ allowDowngrade: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("notifies the user that checking for updates is happening", () => {
|
||||||
|
expect(showInfoNotificationMock).toHaveBeenCalledWith("Checking for updates...");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when no new update is discovered", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
showInfoNotificationMock.mockClear();
|
||||||
|
|
||||||
|
await checkForPlatformUpdatesMock.resolve({
|
||||||
|
updateWasDiscovered: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
await processCheckingForUpdatesPromise;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("notifies the user", () => {
|
||||||
|
expect(showInfoNotificationMock).toHaveBeenCalledWith("No new updates available");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not start downloading update", () => {
|
||||||
|
expect(downloadPlatformUpdateMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when new update is discovered", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await checkForPlatformUpdatesMock.resolve({
|
||||||
|
updateWasDiscovered: true,
|
||||||
|
version: "some-version",
|
||||||
|
});
|
||||||
|
|
||||||
|
await processCheckingForUpdatesPromise;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("starts downloading the update", () => {
|
||||||
|
expect(downloadPlatformUpdateMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("notifies the user that download is happening", () => {
|
||||||
|
expect(showInfoNotificationMock).toHaveBeenCalledWith("Download for version some-version started...");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when download fails", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not quit and install update yet", () => {
|
||||||
|
expect(quitAndInstallUpdateMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("notifies the user about failed download", () => {
|
||||||
|
expect(showInfoNotificationMock).toHaveBeenCalledWith("Download of update failed");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not ask user to install update", () => {
|
||||||
|
expect(askBooleanMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when download succeeds", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not quit and install update yet", () => {
|
||||||
|
expect(quitAndInstallUpdateMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("asks user to install update immediately", () => {
|
||||||
|
expect(askBooleanMock).toHaveBeenCalledWith({
|
||||||
|
title: "Update Available",
|
||||||
|
question:
|
||||||
|
"Version some-version of Lens IDE is available and ready to be installed. Would you like to update now?\n\n" +
|
||||||
|
"Lens should restart automatically, if it doesn't please restart manually. Installed extensions might require updating.",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when user answers to install the update", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await askBooleanMock.resolve(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("quits application and installs the update", () => {
|
||||||
|
expect(quitAndInstallUpdateMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when user answers not to install the update", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await askBooleanMock.resolve(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not quit application and install the update", () => {
|
||||||
|
expect(quitAndInstallUpdateMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
|
||||||
|
import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable";
|
||||||
|
import periodicalCheckForUpdatesInjectable from "../../main/application-update/periodical-check-for-updates/periodical-check-for-updates.injectable";
|
||||||
|
|
||||||
|
const ENOUGH_TIME = 1000 * 60 * 60 * 2;
|
||||||
|
|
||||||
|
describe("periodical checking of updates", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let processCheckingForUpdatesMock: AsyncFnMock<() => Promise<void>>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
mainDi.unoverride(periodicalCheckForUpdatesInjectable);
|
||||||
|
mainDi.permitSideEffects(periodicalCheckForUpdatesInjectable);
|
||||||
|
|
||||||
|
processCheckingForUpdatesMock = asyncFn();
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
processCheckingForUpdatesInjectable,
|
||||||
|
() => processCheckingForUpdatesMock,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given updater is enabled and configuration exists, when started", () => {
|
||||||
|
let rendered: RenderResult;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
|
||||||
|
mainDi.override(publishIsConfiguredInjectable, () => true);
|
||||||
|
});
|
||||||
|
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("checks for updates", () => {
|
||||||
|
expect(processCheckingForUpdatesMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when just not enough time passes, does not check for updates again automatically yet", () => {
|
||||||
|
processCheckingForUpdatesMock.mockClear();
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(ENOUGH_TIME - 1);
|
||||||
|
|
||||||
|
expect(processCheckingForUpdatesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when just enough time passes, checks for updates again automatically", () => {
|
||||||
|
processCheckingForUpdatesMock.mockClear();
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(ENOUGH_TIME);
|
||||||
|
|
||||||
|
expect(processCheckingForUpdatesMock).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given updater is enabled but no configuration exist, when started", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
|
||||||
|
mainDi.override(publishIsConfiguredInjectable, () => false);
|
||||||
|
});
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not check for updates", () => {
|
||||||
|
expect(processCheckingForUpdatesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when time passes, never checks for updates", () => {
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
expect(processCheckingForUpdatesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given updater is not enabled but and configuration exist, when started", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
mainDi.override(electronUpdaterIsActiveInjectable, () => false);
|
||||||
|
mainDi.override(publishIsConfiguredInjectable, () => true);
|
||||||
|
});
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not check for updates", () => {
|
||||||
|
expect(processCheckingForUpdatesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when time passes, never checks for updates", () => {
|
||||||
|
jest.runOnlyPendingTimers();
|
||||||
|
|
||||||
|
expect(processCheckingForUpdatesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,331 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import quitAndInstallUpdateInjectable from "../../main/electron-app/features/quit-and-install-update.injectable";
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import electronUpdaterIsActiveInjectable from "../../main/electron-app/features/electron-updater-is-active.injectable";
|
||||||
|
import publishIsConfiguredInjectable from "../../main/application-update/publish-is-configured.injectable";
|
||||||
|
import type { CheckForPlatformUpdates } from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
|
||||||
|
import checkForPlatformUpdatesInjectable from "../../main/application-update/check-for-platform-updates/check-for-platform-updates.injectable";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import type { UpdateChannel, UpdateChannelId } from "../../common/application-update/update-channels";
|
||||||
|
import { updateChannels } from "../../common/application-update/update-channels";
|
||||||
|
import type { DownloadPlatformUpdate } from "../../main/application-update/download-platform-update/download-platform-update.injectable";
|
||||||
|
import downloadPlatformUpdateInjectable from "../../main/application-update/download-platform-update/download-platform-update.injectable";
|
||||||
|
import selectedUpdateChannelInjectable from "../../common/application-update/selected-update-channel/selected-update-channel.injectable";
|
||||||
|
import type { IComputedValue } from "mobx";
|
||||||
|
import setUpdateOnQuitInjectable from "../../main/electron-app/features/set-update-on-quit.injectable";
|
||||||
|
import type { AskBoolean } from "../../main/ask-boolean/ask-boolean.injectable";
|
||||||
|
import askBooleanInjectable from "../../main/ask-boolean/ask-boolean.injectable";
|
||||||
|
import showInfoNotificationInjectable from "../../renderer/components/notifications/show-info-notification.injectable";
|
||||||
|
import processCheckingForUpdatesInjectable from "../../main/application-update/check-for-updates/process-checking-for-updates.injectable";
|
||||||
|
import appVersionInjectable from "../../common/get-configuration-file-model/app-version/app-version.injectable";
|
||||||
|
|
||||||
|
describe("selection of update stability", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let quitAndInstallUpdateMock: jest.Mock;
|
||||||
|
let checkForPlatformUpdatesMock: AsyncFnMock<CheckForPlatformUpdates>;
|
||||||
|
let downloadPlatformUpdateMock: AsyncFnMock<DownloadPlatformUpdate>;
|
||||||
|
let setUpdateOnQuitMock: jest.Mock;
|
||||||
|
let showInfoNotificationMock: jest.Mock;
|
||||||
|
let askBooleanMock: AsyncFnMock<AskBoolean>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi, rendererDi }) => {
|
||||||
|
quitAndInstallUpdateMock = jest.fn();
|
||||||
|
checkForPlatformUpdatesMock = asyncFn();
|
||||||
|
downloadPlatformUpdateMock = asyncFn();
|
||||||
|
setUpdateOnQuitMock = jest.fn();
|
||||||
|
showInfoNotificationMock = jest.fn(() => () => {});
|
||||||
|
askBooleanMock = asyncFn();
|
||||||
|
|
||||||
|
rendererDi.override(showInfoNotificationInjectable, () => showInfoNotificationMock);
|
||||||
|
|
||||||
|
mainDi.override(askBooleanInjectable, () => askBooleanMock);
|
||||||
|
mainDi.override(setUpdateOnQuitInjectable, () => setUpdateOnQuitMock);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
checkForPlatformUpdatesInjectable,
|
||||||
|
() => checkForPlatformUpdatesMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
downloadPlatformUpdateInjectable,
|
||||||
|
() => downloadPlatformUpdateMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(
|
||||||
|
quitAndInstallUpdateInjectable,
|
||||||
|
() => quitAndInstallUpdateMock,
|
||||||
|
);
|
||||||
|
|
||||||
|
mainDi.override(electronUpdaterIsActiveInjectable, () => true);
|
||||||
|
mainDi.override(publishIsConfiguredInjectable, () => true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when started", () => {
|
||||||
|
let rendered: RenderResult;
|
||||||
|
let processCheckingForUpdates: () => Promise<void>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
|
||||||
|
processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('given update channel "alpha" is selected, when checking for updates', () => {
|
||||||
|
let selectedUpdateChannel: {
|
||||||
|
value: IComputedValue<UpdateChannel>;
|
||||||
|
setValue: (channelId: UpdateChannelId) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
selectedUpdateChannel = applicationBuilder.dis.mainDi.inject(
|
||||||
|
selectedUpdateChannelInjectable,
|
||||||
|
);
|
||||||
|
|
||||||
|
selectedUpdateChannel.setValue(updateChannels.alpha.id);
|
||||||
|
|
||||||
|
processCheckingForUpdates();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks updates from update channel "alpha"', () => {
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(
|
||||||
|
updateChannels.alpha,
|
||||||
|
{ allowDowngrade: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when update is discovered, does not check update from other update channels", async () => {
|
||||||
|
checkForPlatformUpdatesMock.mockClear();
|
||||||
|
|
||||||
|
await checkForPlatformUpdatesMock.resolve({
|
||||||
|
updateWasDiscovered: true,
|
||||||
|
version: "some-version",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(checkForPlatformUpdatesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when no update is discovered", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
checkForPlatformUpdatesMock.mockClear();
|
||||||
|
|
||||||
|
await checkForPlatformUpdatesMock.resolve({
|
||||||
|
updateWasDiscovered: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks updates from update channel "beta"', () => {
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(
|
||||||
|
updateChannels.beta,
|
||||||
|
{ allowDowngrade: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when update is discovered, does not check update from other update channels", async () => {
|
||||||
|
checkForPlatformUpdatesMock.mockClear();
|
||||||
|
|
||||||
|
await checkForPlatformUpdatesMock.resolve({
|
||||||
|
updateWasDiscovered: true,
|
||||||
|
version: "some-version",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(checkForPlatformUpdatesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when no update is discovered again", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
checkForPlatformUpdatesMock.mockClear();
|
||||||
|
|
||||||
|
await checkForPlatformUpdatesMock.resolve({
|
||||||
|
updateWasDiscovered: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('finally checks updates from update channel "latest"', () => {
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(
|
||||||
|
updateChannels.latest,
|
||||||
|
{ allowDowngrade: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when update is discovered, does not check update from other update channels", async () => {
|
||||||
|
checkForPlatformUpdatesMock.mockClear();
|
||||||
|
|
||||||
|
await checkForPlatformUpdatesMock.resolve({
|
||||||
|
updateWasDiscovered: true,
|
||||||
|
version: "some-version",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(checkForPlatformUpdatesMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('given update channel "beta" is selected', () => {
|
||||||
|
let selectedUpdateChannel: {
|
||||||
|
value: IComputedValue<UpdateChannel>;
|
||||||
|
setValue: (channelId: UpdateChannelId) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
selectedUpdateChannel = applicationBuilder.dis.mainDi.inject(
|
||||||
|
selectedUpdateChannelInjectable,
|
||||||
|
);
|
||||||
|
|
||||||
|
selectedUpdateChannel.setValue(updateChannels.beta.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when checking for updates", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
processCheckingForUpdates();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when update from "beta" channel is discovered', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await checkForPlatformUpdatesMock.resolve({
|
||||||
|
updateWasDiscovered: true,
|
||||||
|
version: "some-beta-version",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when update is downloaded", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await downloadPlatformUpdateMock.resolve({ downloadWasSuccessful: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when user would close the application, installs the update", () => {
|
||||||
|
expect(setUpdateOnQuitMock).toHaveBeenLastCalledWith(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('given user changes update channel to "latest", when user would close the application, does not install the update for not being stable enough', () => {
|
||||||
|
selectedUpdateChannel.setValue(updateChannels.latest.id);
|
||||||
|
|
||||||
|
expect(setUpdateOnQuitMock).toHaveBeenLastCalledWith(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('given user changes update channel to "alpha", when user would close the application, installs the update for being stable enough', () => {
|
||||||
|
selectedUpdateChannel.setValue(updateChannels.alpha.id);
|
||||||
|
|
||||||
|
expect(setUpdateOnQuitMock).toHaveBeenLastCalledWith(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given valid update channel selection is stored, when checking for updates, checks for updates from the update channel", async () => {
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
// TODO: Switch to more natural way of setting initial value
|
||||||
|
// TODO: UserStore is currently responsible for getting and setting initial value
|
||||||
|
const selectedUpdateChannel = mainDi.inject(selectedUpdateChannelInjectable);
|
||||||
|
|
||||||
|
selectedUpdateChannel.setValue(updateChannels.beta.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
|
||||||
|
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
|
||||||
|
|
||||||
|
processCheckingForUpdates();
|
||||||
|
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(updateChannels.beta, expect.any(Object));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given invalid update channel selection is stored, when checking for updates, checks for updates from the update channel", async () => {
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
// TODO: Switch to more natural way of setting initial value
|
||||||
|
// TODO: UserStore is currently responsible for getting and setting initial value
|
||||||
|
const selectedUpdateChannel = mainDi.inject(selectedUpdateChannelInjectable);
|
||||||
|
|
||||||
|
selectedUpdateChannel.setValue("something-invalid" as UpdateChannelId);
|
||||||
|
});
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
|
||||||
|
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
|
||||||
|
|
||||||
|
processCheckingForUpdates();
|
||||||
|
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(updateChannels.latest, expect.any(Object));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('given no update channel selection is stored and currently using stable release, when user checks for updates, checks for updates from "latest" update channel by default', async () => {
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
mainDi.override(appVersionInjectable, () => "1.0.0");
|
||||||
|
});
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
|
||||||
|
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
|
||||||
|
|
||||||
|
processCheckingForUpdates();
|
||||||
|
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(
|
||||||
|
updateChannels.latest,
|
||||||
|
{ allowDowngrade: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('given no update channel selection is stored and currently using alpha release, when checking for updates, checks for updates from "alpha" channel', async () => {
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
mainDi.override(appVersionInjectable, () => "1.0.0-alpha");
|
||||||
|
});
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
|
||||||
|
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
|
||||||
|
|
||||||
|
processCheckingForUpdates();
|
||||||
|
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(updateChannels.alpha, expect.any(Object));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('given no update channel selection is stored and currently using beta release, when checking for updates, checks for updates from "beta" channel', async () => {
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
mainDi.override(appVersionInjectable, () => "1.0.0-beta");
|
||||||
|
});
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
|
||||||
|
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
|
||||||
|
|
||||||
|
processCheckingForUpdates();
|
||||||
|
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(updateChannels.beta, expect.any(Object));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given update channel selection is stored and currently using prerelease, when checking for updates, checks for updates from stored channel", async () => {
|
||||||
|
applicationBuilder.beforeApplicationStart(({ mainDi }) => {
|
||||||
|
mainDi.override(appVersionInjectable, () => "1.0.0-alpha");
|
||||||
|
|
||||||
|
// TODO: Switch to more natural way of setting initial value
|
||||||
|
// TODO: UserStore is currently responsible for getting and setting initial value
|
||||||
|
const selectedUpdateChannel = mainDi.inject(selectedUpdateChannelInjectable);
|
||||||
|
|
||||||
|
selectedUpdateChannel.setValue(updateChannels.beta.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
|
||||||
|
const processCheckingForUpdates = applicationBuilder.dis.mainDi.inject(processCheckingForUpdatesInjectable);
|
||||||
|
|
||||||
|
processCheckingForUpdates();
|
||||||
|
|
||||||
|
expect(checkForPlatformUpdatesMock).toHaveBeenCalledWith(updateChannels.beta, expect.any(Object));
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -328,6 +328,9 @@ exports[`cluster - order of sidebar items when rendered renders 1`] = `
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -723,5 +726,8 @@ exports[`cluster - order of sidebar items when rendered when parent is expanded
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -293,6 +293,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -589,6 +592,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -909,6 +915,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1234,6 +1243,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
<div
|
<div
|
||||||
data-testid="some-child-page"
|
data-testid="some-child-page"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1534,6 +1546,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
<div
|
<div
|
||||||
data-testid="some-child-page"
|
data-testid="some-child-page"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1854,6 +1869,9 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -2150,5 +2168,8 @@ exports[`cluster - sidebar and tab navigation for core given core registrations
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -293,6 +293,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -589,6 +592,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -929,6 +935,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1313,6 +1322,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1697,6 +1709,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -2036,6 +2051,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -2376,6 +2394,9 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -2672,5 +2693,8 @@ exports[`cluster - sidebar and tab navigation for extensions given extension wit
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -261,6 +261,9 @@ exports[`cluster - visibility of sidebar items given kube resource for route is
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -573,5 +576,8 @@ exports[`cluster - visibility of sidebar items given kube resource for route is
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`extensions - navigation using application menu renders 1`] = `<div />`;
|
exports[`extensions - navigation using application menu renders 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`extensions - navigation using application menu when navigating to extensions using application menu renders 1`] = `
|
exports[`extensions - navigation using application menu when navigating to extensions using application menu renders 1`] = `
|
||||||
<div>
|
<div>
|
||||||
@ -118,5 +124,8 @@ exports[`extensions - navigation using application menu when navigating to exten
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -6,12 +6,7 @@
|
|||||||
import type { RenderResult } from "@testing-library/react";
|
import type { RenderResult } from "@testing-library/react";
|
||||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
|
import focusWindowInjectable from "../../renderer/navigation/focus-window.injectable";
|
||||||
import extensionsStoreInjectable from "../../extensions/extensions-store/extensions-store.injectable";
|
|
||||||
import type { ExtensionsStore } from "../../extensions/extensions-store/extensions-store";
|
|
||||||
import fileSystemProvisionerStoreInjectable from "../../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable";
|
|
||||||
import type { FileSystemProvisionerStore } from "../../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store";
|
|
||||||
import focusWindowInjectable from "../../renderer/ipc-channel-listeners/focus-window.injectable";
|
|
||||||
|
|
||||||
// TODO: Make components free of side effects by making them deterministic
|
// TODO: Make components free of side effects by making them deterministic
|
||||||
jest.mock("../../renderer/components/input/input");
|
jest.mock("../../renderer/components/input/input");
|
||||||
@ -22,11 +17,7 @@ describe("extensions - navigation using application menu", () => {
|
|||||||
let focusWindowMock: jest.Mock;
|
let focusWindowMock: jest.Mock;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
applicationBuilder = getApplicationBuilder().beforeApplicationStart(({ mainDi, rendererDi }) => {
|
applicationBuilder = getApplicationBuilder().beforeApplicationStart(({ rendererDi }) => {
|
||||||
mainDi.override(isAutoUpdateEnabledInjectable, () => () => false);
|
|
||||||
rendererDi.override(extensionsStoreInjectable, () => ({}) as unknown as ExtensionsStore);
|
|
||||||
rendererDi.override(fileSystemProvisionerStoreInjectable, () => ({}) as unknown as FileSystemProvisionerStore);
|
|
||||||
|
|
||||||
focusWindowMock = jest.fn();
|
focusWindowMock = jest.fn();
|
||||||
|
|
||||||
rendererDi.override(focusWindowInjectable, () => focusWindowMock);
|
rendererDi.override(focusWindowInjectable, () => focusWindowMock);
|
||||||
|
|||||||
@ -454,5 +454,8 @@ exports[`helm-charts - navigation to Helm charts when navigating to Helm charts
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -356,13 +356,12 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -370,7 +369,6 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -537,6 +535,9 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -679,6 +680,9 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -687,6 +691,9 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
<div>
|
<div>
|
||||||
Some front page
|
Some front page
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -695,6 +702,9 @@ exports[`preferences - closing-preferences given accessing preferences directly
|
|||||||
<div>
|
<div>
|
||||||
Some front page
|
Some front page
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1054,13 +1064,12 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -1068,7 +1077,6 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -1235,6 +1243,9 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1377,6 +1388,9 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1519,6 +1533,9 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1661,5 +1678,8 @@ exports[`preferences - closing-preferences given already in a page and then navi
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -199,6 +199,9 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -546,13 +549,12 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -560,7 +562,6 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -727,5 +728,8 @@ exports[`preferences - navigation to application preferences given in some child
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -344,13 +344,12 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -358,7 +357,6 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -525,6 +523,9 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -935,5 +936,8 @@ exports[`preferences - navigation to editor preferences given in preferences, wh
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -344,13 +344,12 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -358,7 +357,6 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -525,6 +523,9 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -884,13 +885,12 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -898,7 +898,6 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -1065,6 +1064,9 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1239,5 +1241,8 @@ exports[`preferences - navigation to extension specific preferences given in pre
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -344,13 +344,12 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -358,7 +357,6 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -525,6 +523,9 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -836,7 +837,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
class="flex gaps"
|
class="flex gaps"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select theme-lens box grow css-b62m3t-container"
|
class="Select theme-lens box grow Select--is-disabled css-3iigni-container"
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
@ -849,7 +850,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
class="css-1f43avz-a11yText-A11yText"
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control Select__control--is-disabled css-1insrsq-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container css-319lph-ValueContainer"
|
||||||
@ -861,7 +862,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
Repositories
|
Repositories
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-jzldcf-Input"
|
||||||
data-value=""
|
data-value=""
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@ -873,6 +874,7 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
class="Select__input"
|
class="Select__input"
|
||||||
|
disabled=""
|
||||||
id="HelmRepoSelect"
|
id="HelmRepoSelect"
|
||||||
role="combobox"
|
role="combobox"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
@ -886,8 +888,22 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
<div
|
<div
|
||||||
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
|
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||||
>
|
>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
class="Select__indicator Select__loading-indicator css-at12u2-loadingIndicator"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="css-1xtdfmb-LoadingDot"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="css-zoievk-LoadingDot"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
class="css-x748d8-LoadingDot"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<span
|
<span
|
||||||
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
|
class="Select__indicator-separator css-109onse-indicatorSeparator"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
@ -920,13 +936,11 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
class="repos"
|
class="repos"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="notice"
|
class="pt-5 relative"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex-grow text-center"
|
class="Spinner singleColor center"
|
||||||
>
|
/>
|
||||||
The repositories have not been added yet
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -969,5 +983,8 @@ exports[`preferences - navigation to kubernetes preferences given in preferences
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -344,13 +344,12 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -358,7 +357,6 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -525,6 +523,9 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -727,5 +728,8 @@ exports[`preferences - navigation to proxy preferences given in preferences, whe
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -185,6 +185,9 @@ exports[`preferences - navigation to telemetry preferences given URL for Sentry
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -532,13 +535,12 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -546,7 +548,6 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -713,6 +714,9 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1072,13 +1076,12 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -1086,7 +1089,6 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -1253,6 +1255,9 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1429,6 +1434,9 @@ exports[`preferences - navigation to telemetry preferences given in preferences,
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -1568,5 +1576,8 @@ exports[`preferences - navigation to telemetry preferences given no URL for Sent
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -344,13 +344,12 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -358,7 +357,6 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -525,6 +523,9 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -845,5 +846,8 @@ exports[`preferences - navigation to terminal preferences given in preferences,
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`preferences - navigation using application menu renders 1`] = `<div />`;
|
exports[`preferences - navigation using application menu renders 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`preferences - navigation using application menu when navigating to preferences using application menu renders 1`] = `
|
exports[`preferences - navigation using application menu when navigating to preferences using application menu renders 1`] = `
|
||||||
<div>
|
<div>
|
||||||
@ -346,13 +352,12 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
class="Select__control css-1s2u09g-control"
|
class="Select__control css-1s2u09g-control"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__value-container css-319lph-ValueContainer"
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="Select__placeholder css-14el2xx-placeholder"
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
id="react-select-update-channel-input-placeholder"
|
|
||||||
>
|
>
|
||||||
Select...
|
Stable
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="Select__input-container css-6j8wv5-Input"
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
@ -360,7 +365,6 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
aria-describedby="react-select-update-channel-input-placeholder"
|
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
@ -527,5 +531,8 @@ exports[`preferences - navigation using application menu when navigating to pref
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -0,0 +1,542 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`show-about-using-tray renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`show-about-using-tray when navigating using tray renders 1`] = `
|
||||||
|
<body>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="SettingLayout showNavigation Preferences"
|
||||||
|
data-testid="application-preferences-page"
|
||||||
|
>
|
||||||
|
<nav
|
||||||
|
class="sidebarRegion"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="sidebar"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Tabs flex column"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="header"
|
||||||
|
>
|
||||||
|
Preferences
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Tab flex gaps align-center active"
|
||||||
|
data-testid="tab-link-for-application"
|
||||||
|
role="tab"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="label"
|
||||||
|
>
|
||||||
|
App
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Tab flex gaps align-center"
|
||||||
|
data-testid="tab-link-for-proxy"
|
||||||
|
role="tab"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="label"
|
||||||
|
>
|
||||||
|
Proxy
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Tab flex gaps align-center"
|
||||||
|
data-testid="tab-link-for-kubernetes"
|
||||||
|
role="tab"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="label"
|
||||||
|
>
|
||||||
|
Kubernetes
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Tab flex gaps align-center"
|
||||||
|
data-testid="tab-link-for-editor"
|
||||||
|
role="tab"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="label"
|
||||||
|
>
|
||||||
|
Editor
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Tab flex gaps align-center"
|
||||||
|
data-testid="tab-link-for-terminal"
|
||||||
|
role="tab"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="label"
|
||||||
|
>
|
||||||
|
Terminal
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div
|
||||||
|
class="contentRegion"
|
||||||
|
id="ScrollSpyRoot"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="content"
|
||||||
|
>
|
||||||
|
<section
|
||||||
|
id="application"
|
||||||
|
>
|
||||||
|
<h2
|
||||||
|
data-testid="application-header"
|
||||||
|
>
|
||||||
|
Application
|
||||||
|
</h2>
|
||||||
|
<section
|
||||||
|
id="appearance"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="SubTitle"
|
||||||
|
>
|
||||||
|
Theme
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select theme-lens css-b62m3t-container"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
|
id="react-select-theme-input-live-region"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
aria-atomic="false"
|
||||||
|
aria-live="polite"
|
||||||
|
aria-relevant="additions text"
|
||||||
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="Select__control css-1s2u09g-control"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select__value-container css-319lph-ValueContainer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
|
id="react-select-theme-input-placeholder"
|
||||||
|
>
|
||||||
|
Select...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
|
data-value=""
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-autocomplete="list"
|
||||||
|
aria-describedby="react-select-theme-input-placeholder"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-haspopup="true"
|
||||||
|
autocapitalize="none"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
class="Select__input"
|
||||||
|
id="theme-input"
|
||||||
|
role="combobox"
|
||||||
|
spellcheck="false"
|
||||||
|
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
|
||||||
|
tabindex="0"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="css-tj5bde-Svg"
|
||||||
|
focusable="false"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
width="20"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section
|
||||||
|
id="extensionRegistryUrl"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="SubTitle"
|
||||||
|
>
|
||||||
|
Extension Install Registry
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select theme-lens css-b62m3t-container"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
|
id="react-select-extension-install-registry-input-live-region"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
aria-atomic="false"
|
||||||
|
aria-live="polite"
|
||||||
|
aria-relevant="additions text"
|
||||||
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="Select__control css-1s2u09g-control"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select__value-container css-319lph-ValueContainer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
|
id="react-select-extension-install-registry-input-placeholder"
|
||||||
|
>
|
||||||
|
Select...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
|
data-value=""
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-autocomplete="list"
|
||||||
|
aria-describedby="react-select-extension-install-registry-input-placeholder"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-haspopup="true"
|
||||||
|
autocapitalize="none"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
class="Select__input"
|
||||||
|
id="extension-install-registry-input"
|
||||||
|
role="combobox"
|
||||||
|
spellcheck="false"
|
||||||
|
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
|
||||||
|
tabindex="0"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="css-tj5bde-Svg"
|
||||||
|
focusable="false"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
width="20"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p
|
||||||
|
class="mt-4 mb-5 leading-relaxed"
|
||||||
|
>
|
||||||
|
This setting is to change the registry URL for installing extensions by name.
|
||||||
|
If you are unable to access the default registry (https://registry.npmjs.org) you can change it in your
|
||||||
|
<b>
|
||||||
|
.npmrc
|
||||||
|
</b>
|
||||||
|
file or in the input below.
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
class="Input theme round black disabled invalid"
|
||||||
|
>
|
||||||
|
<label
|
||||||
|
class="input-area flex gaps align-center"
|
||||||
|
id=""
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
class="input box grow"
|
||||||
|
disabled=""
|
||||||
|
placeholder="Custom Extension Registry URL..."
|
||||||
|
spellcheck="false"
|
||||||
|
value="some-custom-url"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="input-info flex gaps"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section
|
||||||
|
id="other"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="SubTitle"
|
||||||
|
>
|
||||||
|
Start-up
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<label
|
||||||
|
class="Switch"
|
||||||
|
data-testid="switch"
|
||||||
|
>
|
||||||
|
Automatically start Lens on login
|
||||||
|
<input
|
||||||
|
role="switch"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section
|
||||||
|
id="update-channel"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="SubTitle"
|
||||||
|
>
|
||||||
|
Update Channel
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select theme-lens css-b62m3t-container"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
|
id="react-select-update-channel-input-live-region"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
aria-atomic="false"
|
||||||
|
aria-live="polite"
|
||||||
|
aria-relevant="additions text"
|
||||||
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="Select__control css-1s2u09g-control"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select__value-container Select__value-container--has-value css-319lph-ValueContainer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select__single-value css-qc6sy-singleValue"
|
||||||
|
>
|
||||||
|
Stable
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
|
data-value=""
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-autocomplete="list"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-haspopup="true"
|
||||||
|
autocapitalize="none"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
class="Select__input"
|
||||||
|
id="update-channel-input"
|
||||||
|
role="combobox"
|
||||||
|
spellcheck="false"
|
||||||
|
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
|
||||||
|
tabindex="0"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="css-tj5bde-Svg"
|
||||||
|
focusable="false"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
width="20"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<hr />
|
||||||
|
<section
|
||||||
|
id="locale"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="SubTitle"
|
||||||
|
>
|
||||||
|
Locale Timezone
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select theme-lens css-b62m3t-container"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
|
id="react-select-timezone-input-live-region"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
aria-atomic="false"
|
||||||
|
aria-live="polite"
|
||||||
|
aria-relevant="additions text"
|
||||||
|
class="css-1f43avz-a11yText-A11yText"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="Select__control css-1s2u09g-control"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select__value-container css-319lph-ValueContainer"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="Select__placeholder css-14el2xx-placeholder"
|
||||||
|
id="react-select-timezone-input-placeholder"
|
||||||
|
>
|
||||||
|
Select...
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select__input-container css-6j8wv5-Input"
|
||||||
|
data-value=""
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
aria-autocomplete="list"
|
||||||
|
aria-describedby="react-select-timezone-input-placeholder"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-haspopup="true"
|
||||||
|
autocapitalize="none"
|
||||||
|
autocomplete="off"
|
||||||
|
autocorrect="off"
|
||||||
|
class="Select__input"
|
||||||
|
id="timezone-input"
|
||||||
|
role="combobox"
|
||||||
|
spellcheck="false"
|
||||||
|
style="opacity: 1; width: 100%; grid-area: 1 / 2; min-width: 2px; border: 0px; margin: 0px; outline: 0; padding: 0px;"
|
||||||
|
tabindex="0"
|
||||||
|
type="text"
|
||||||
|
value=""
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Select__indicators css-1hb7zxy-IndicatorsContainer"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="Select__indicator-separator css-1okebmr-indicatorSeparator"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
class="Select__indicator Select__dropdown-indicator css-tlfecz-indicatorContainer"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
aria-hidden="true"
|
||||||
|
class="css-tj5bde-Svg"
|
||||||
|
focusable="false"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
width="20"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4.516 7.548c0.436-0.446 1.043-0.481 1.576 0l3.908 3.747 3.908-3.747c0.533-0.481 1.141-0.446 1.574 0 0.436 0.445 0.408 1.197 0 1.615-0.406 0.418-4.695 4.502-4.695 4.502-0.217 0.223-0.502 0.335-0.787 0.335s-0.57-0.112-0.789-0.335c0 0-4.287-4.084-4.695-4.502s-0.436-1.17 0-1.615z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="toolsRegion"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="fixed top-[60px]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
data-testid="close-preferences"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-label="Close"
|
||||||
|
class="closeButton"
|
||||||
|
role="button"
|
||||||
|
>
|
||||||
|
<i
|
||||||
|
class="Icon icon material focusable"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="icon"
|
||||||
|
data-icon-name="close"
|
||||||
|
>
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
class="esc"
|
||||||
|
>
|
||||||
|
ESC
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
`;
|
||||||
@ -5,17 +5,12 @@
|
|||||||
import type { RenderResult } from "@testing-library/react";
|
import type { RenderResult } from "@testing-library/react";
|
||||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import defaultShellInjectable from "../../renderer/components/+preferences/default-shell.injectable";
|
|
||||||
|
|
||||||
describe("preferences - navigation to terminal preferences", () => {
|
describe("preferences - navigation to terminal preferences", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
applicationBuilder = getApplicationBuilder();
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
applicationBuilder.beforeApplicationStart(({ rendererDi }) => {
|
|
||||||
rendererDi.override(defaultShellInjectable, () => "some-default-shell");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("given in preferences, when rendered", () => {
|
describe("given in preferences, when rendered", () => {
|
||||||
|
|||||||
44
src/behaviours/preferences/navigation-using-tray.test.ts
Normal file
44
src/behaviours/preferences/navigation-using-tray.test.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { RenderResult } from "@testing-library/react";
|
||||||
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
|
|
||||||
|
describe("show-about-using-tray", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
let rendered: RenderResult;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
rendered = await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not show application preferences page yet", () => {
|
||||||
|
const actual = rendered.queryByTestId("application-preferences-page");
|
||||||
|
|
||||||
|
expect(actual).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when navigating using tray", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await applicationBuilder.tray.click("open-preferences");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders", () => {
|
||||||
|
expect(rendered.baseElement).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("shows application preferences page", () => {
|
||||||
|
const actual = rendered.getByTestId("application-preferences-page");
|
||||||
|
|
||||||
|
expect(actual).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,6 +1,12 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`welcome - navigation using application menu renders 1`] = `<div />`;
|
exports[`welcome - navigation using application menu renders 1`] = `
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`welcome - navigation using application menu when navigating to welcome using application menu renders 1`] = `
|
exports[`welcome - navigation using application menu when navigating to welcome using application menu renders 1`] = `
|
||||||
<div>
|
<div>
|
||||||
@ -87,5 +93,8 @@ exports[`welcome - navigation using application menu when navigating to welcome
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
class="Notifications flex column align-flex-end"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -6,16 +6,13 @@
|
|||||||
import type { RenderResult } from "@testing-library/react";
|
import type { RenderResult } from "@testing-library/react";
|
||||||
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
|
||||||
import isAutoUpdateEnabledInjectable from "../../main/is-auto-update-enabled.injectable";
|
|
||||||
|
|
||||||
describe("welcome - navigation using application menu", () => {
|
describe("welcome - navigation using application menu", () => {
|
||||||
let applicationBuilder: ApplicationBuilder;
|
let applicationBuilder: ApplicationBuilder;
|
||||||
let rendered: RenderResult;
|
let rendered: RenderResult;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
applicationBuilder = getApplicationBuilder().beforeApplicationStart(({ mainDi }) => {
|
applicationBuilder = getApplicationBuilder();
|
||||||
mainDi.override(isAutoUpdateEnabledInjectable, () => () => false);
|
|
||||||
});
|
|
||||||
|
|
||||||
rendered = await applicationBuilder.render();
|
rendered = await applicationBuilder.render();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -369,6 +369,8 @@ users:
|
|||||||
|
|
||||||
mockFs(mockOpts);
|
mockFs(mockOpts);
|
||||||
|
|
||||||
|
mainDi.override(appVersionInjectable, () => "3.6.0");
|
||||||
|
|
||||||
createCluster = mainDi.inject(createClusterInjectionToken);
|
createCluster = mainDi.inject(createClusterInjectionToken);
|
||||||
|
|
||||||
clusterStore = mainDi.inject(clusterStoreInjectable);
|
clusterStore = mainDi.inject(clusterStoreInjectable);
|
||||||
|
|||||||
@ -21,7 +21,7 @@ jest.mock("electron", () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
import { UserStore } from "../user-store";
|
import type { UserStore } from "../user-store";
|
||||||
import { Console } from "console";
|
import { Console } from "console";
|
||||||
import { SemVer } from "semver";
|
import { SemVer } from "semver";
|
||||||
import electron from "electron";
|
import electron from "electron";
|
||||||
@ -49,14 +49,15 @@ describe("user store tests", () => {
|
|||||||
|
|
||||||
di.override(writeFileInjectable, () => () => Promise.resolve());
|
di.override(writeFileInjectable, () => () => Promise.resolve());
|
||||||
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data");
|
||||||
di.override(userStoreInjectable, () => UserStore.createInstance());
|
|
||||||
|
|
||||||
di.permitSideEffects(getConfigurationFileModelInjectable);
|
di.permitSideEffects(getConfigurationFileModelInjectable);
|
||||||
|
|
||||||
di.permitSideEffects(appVersionInjectable);
|
di.permitSideEffects(appVersionInjectable);
|
||||||
|
di.permitSideEffects(userStoreInjectable);
|
||||||
|
|
||||||
|
di.unoverride(userStoreInjectable);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
UserStore.resetInstance();
|
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -126,6 +127,8 @@ describe("user store tests", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
di.override(appVersionInjectable, () => "10.0.0");
|
||||||
|
|
||||||
userStore = di.inject(userStoreInjectable);
|
userStore = di.inject(userStoreInjectable);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4,12 +4,9 @@
|
|||||||
*/
|
*/
|
||||||
import { getInjectionToken } from "@ogre-tools/injectable";
|
import { getInjectionToken } from "@ogre-tools/injectable";
|
||||||
import type { PathName } from "./app-path-names";
|
import type { PathName } from "./app-path-names";
|
||||||
import { createChannel } from "../ipc-channel/create-channel/create-channel";
|
|
||||||
|
|
||||||
export type AppPaths = Record<PathName, string>;
|
export type AppPaths = Record<PathName, string>;
|
||||||
|
|
||||||
export const appPathsInjectionToken = getInjectionToken<AppPaths>({ id: "app-paths-token" });
|
export const appPathsInjectionToken = getInjectionToken<AppPaths>({ id: "app-paths-token" });
|
||||||
|
|
||||||
export const appPathsIpcChannel = createChannel<AppPaths>("app-paths");
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
22
src/common/app-paths/app-paths-channel.injectable.ts
Normal file
22
src/common/app-paths/app-paths-channel.injectable.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* 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 "./app-path-injection-token";
|
||||||
|
import type { RequestChannel } from "../utils/channel/request-channel-injection-token";
|
||||||
|
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
|
||||||
|
|
||||||
|
export type AppPathsChannel = RequestChannel<void, AppPaths>;
|
||||||
|
|
||||||
|
const appPathsChannelInjectable = getInjectable({
|
||||||
|
id: "app-paths-channel",
|
||||||
|
|
||||||
|
instantiate: (): AppPathsChannel => ({
|
||||||
|
id: "app-paths",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: messageChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default appPathsChannelInjectable;
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 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 { MessageChannel } from "../utils/channel/message-channel-injection-token";
|
||||||
|
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
|
||||||
|
|
||||||
|
export type ApplicationUpdateStatusEventId =
|
||||||
|
| "checking-for-updates"
|
||||||
|
| "no-updates-available"
|
||||||
|
| "download-for-update-started"
|
||||||
|
| "download-for-update-failed";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||||
|
export type ApplicationUpdateStatusChannelMessage = { eventId: ApplicationUpdateStatusEventId; version?: string };
|
||||||
|
export type ApplicationUpdateStatusChannel = MessageChannel<ApplicationUpdateStatusChannelMessage>;
|
||||||
|
|
||||||
|
const applicationUpdateStatusChannelInjectable = getInjectable({
|
||||||
|
id: "application-update-status-channel",
|
||||||
|
|
||||||
|
instantiate: (): ApplicationUpdateStatusChannel => ({
|
||||||
|
id: "application-update-status-channel",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: messageChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default applicationUpdateStatusChannelInjectable;
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* 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 createSyncBoxInjectable from "../../utils/sync-box/create-sync-box.injectable";
|
||||||
|
import type { UpdateChannel } from "../update-channels";
|
||||||
|
import { syncBoxInjectionToken } from "../../utils/sync-box/sync-box-injection-token";
|
||||||
|
|
||||||
|
const discoveredUpdateVersionInjectable = getInjectable({
|
||||||
|
id: "discovered-update-version",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const createSyncBox = di.inject(createSyncBoxInjectable);
|
||||||
|
|
||||||
|
return createSyncBox<
|
||||||
|
| { version: string; updateChannel: UpdateChannel }
|
||||||
|
| null
|
||||||
|
>(
|
||||||
|
"discovered-update-version",
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: syncBoxInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default discoveredUpdateVersionInjectable;
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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 createSyncBoxInjectable from "../../utils/sync-box/create-sync-box.injectable";
|
||||||
|
import { syncBoxInjectionToken } from "../../utils/sync-box/sync-box-injection-token";
|
||||||
|
|
||||||
|
export interface ProgressOfDownload {
|
||||||
|
percentage: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const progressOfUpdateDownloadInjectable = getInjectable({
|
||||||
|
id: "progress-of-update-download-state",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const createSyncBox = di.inject(createSyncBoxInjectable);
|
||||||
|
|
||||||
|
return createSyncBox<ProgressOfDownload>("progress-of-update-download", { percentage: 0 });
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: syncBoxInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default progressOfUpdateDownloadInjectable;
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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 { SemVer } from "semver";
|
||||||
|
import appVersionInjectable from "../../get-configuration-file-model/app-version/app-version.injectable";
|
||||||
|
import type { UpdateChannelId } from "../update-channels";
|
||||||
|
import { updateChannels } from "../update-channels";
|
||||||
|
|
||||||
|
const defaultUpdateChannelInjectable = getInjectable({
|
||||||
|
id: "default-update-channel",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const appVersion = di.inject(appVersionInjectable);
|
||||||
|
|
||||||
|
const currentReleaseChannel = new SemVer(appVersion).prerelease[0]?.toString() as UpdateChannelId;
|
||||||
|
|
||||||
|
if (currentReleaseChannel && updateChannels[currentReleaseChannel]) {
|
||||||
|
return updateChannels[currentReleaseChannel];
|
||||||
|
}
|
||||||
|
|
||||||
|
return updateChannels.latest;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default defaultUpdateChannelInjectable;
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* 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 { IComputedValue } from "mobx";
|
||||||
|
import { action, computed, observable } from "mobx";
|
||||||
|
import type { UpdateChannel, UpdateChannelId } from "../update-channels";
|
||||||
|
import { updateChannels } from "../update-channels";
|
||||||
|
import defaultUpdateChannelInjectable from "./default-update-channel.injectable";
|
||||||
|
|
||||||
|
export interface SelectedUpdateChannel {
|
||||||
|
value: IComputedValue<UpdateChannel>;
|
||||||
|
setValue: (channelId?: UpdateChannelId) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedUpdateChannelInjectable = getInjectable({
|
||||||
|
id: "selected-update-channel",
|
||||||
|
|
||||||
|
instantiate: (di): SelectedUpdateChannel => {
|
||||||
|
const defaultUpdateChannel = di.inject(defaultUpdateChannelInjectable);
|
||||||
|
const state = observable.box(defaultUpdateChannel);
|
||||||
|
|
||||||
|
return {
|
||||||
|
value: computed(() => state.get()),
|
||||||
|
|
||||||
|
setValue: action((channelId) => {
|
||||||
|
const targetUpdateChannel =
|
||||||
|
channelId && updateChannels[channelId]
|
||||||
|
? updateChannels[channelId]
|
||||||
|
: defaultUpdateChannel;
|
||||||
|
|
||||||
|
state.set(targetUpdateChannel);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default selectedUpdateChannelInjectable;
|
||||||
36
src/common/application-update/update-channels.ts
Normal file
36
src/common/application-update/update-channels.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type UpdateChannelId = "alpha" | "beta" | "latest";
|
||||||
|
|
||||||
|
const latestChannel: UpdateChannel = {
|
||||||
|
id: "latest",
|
||||||
|
label: "Stable",
|
||||||
|
moreStableUpdateChannel: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const betaChannel: UpdateChannel = {
|
||||||
|
id: "beta",
|
||||||
|
label: "Beta",
|
||||||
|
moreStableUpdateChannel: latestChannel,
|
||||||
|
};
|
||||||
|
|
||||||
|
const alphaChannel: UpdateChannel = {
|
||||||
|
id: "alpha",
|
||||||
|
label: "Alpha",
|
||||||
|
moreStableUpdateChannel: betaChannel,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateChannels: Record<UpdateChannelId, UpdateChannel> = {
|
||||||
|
latest: latestChannel,
|
||||||
|
beta: betaChannel,
|
||||||
|
alpha: alphaChannel,
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface UpdateChannel {
|
||||||
|
readonly id: UpdateChannelId;
|
||||||
|
readonly label: string;
|
||||||
|
readonly moreStableUpdateChannel: UpdateChannel | null;
|
||||||
|
}
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 createSyncBoxInjectable from "../../utils/sync-box/create-sync-box.injectable";
|
||||||
|
import { syncBoxInjectionToken } from "../../utils/sync-box/sync-box-injection-token";
|
||||||
|
|
||||||
|
const updateIsBeingDownloadedInjectable = getInjectable({
|
||||||
|
id: "update-is-being-downloaded",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const createSyncBox = di.inject(createSyncBoxInjectable);
|
||||||
|
|
||||||
|
return createSyncBox("update-is-being-downloaded", false);
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: syncBoxInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default updateIsBeingDownloadedInjectable;
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 createSyncBoxInjectable from "../../utils/sync-box/create-sync-box.injectable";
|
||||||
|
import { syncBoxInjectionToken } from "../../utils/sync-box/sync-box-injection-token";
|
||||||
|
|
||||||
|
const updatesAreBeingDiscoveredInjectable = getInjectable({
|
||||||
|
id: "updates-are-being-discovered",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const createSyncBox = di.inject(createSyncBoxInjectable);
|
||||||
|
|
||||||
|
return createSyncBox("updates-are-being-discovered", false);
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: syncBoxInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default updatesAreBeingDiscoveredInjectable;
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { MessageChannel } from "../utils/channel/message-channel-injection-token";
|
||||||
|
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
|
||||||
|
|
||||||
|
export type AskBooleanAnswerChannel = MessageChannel<{ id: string; value: boolean }>;
|
||||||
|
|
||||||
|
const askBooleanAnswerChannelInjectable = getInjectable({
|
||||||
|
id: "ask-boolean-answer-channel",
|
||||||
|
|
||||||
|
instantiate: (): AskBooleanAnswerChannel => ({
|
||||||
|
id: "ask-boolean-answer",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: messageChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default askBooleanAnswerChannelInjectable;
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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 { MessageChannel } from "../utils/channel/message-channel-injection-token";
|
||||||
|
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
|
||||||
|
export type AskBooleanQuestionParameters = { id: string; title: string; question: string };
|
||||||
|
export type AskBooleanQuestionChannel = MessageChannel<AskBooleanQuestionParameters>;
|
||||||
|
|
||||||
|
const askBooleanQuestionChannelInjectable = getInjectable({
|
||||||
|
id: "ask-boolean-question-channel",
|
||||||
|
|
||||||
|
instantiate: (): AskBooleanQuestionChannel => ({
|
||||||
|
id: "ask-boolean-question",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: messageChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default askBooleanQuestionChannelInjectable;
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* 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 { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
|
||||||
|
import type { MessageChannel } from "../utils/channel/message-channel-injection-token";
|
||||||
|
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
|
||||||
|
|
||||||
|
export type AppNavigationChannel = MessageChannel<string>;
|
||||||
|
|
||||||
|
const appNavigationChannelInjectable = getInjectable({
|
||||||
|
id: "app-navigation-channel",
|
||||||
|
|
||||||
|
instantiate: (): AppNavigationChannel => ({
|
||||||
|
id: IpcRendererNavigationEvents.NAVIGATE_IN_APP,
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: messageChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default appNavigationChannelInjectable;
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* 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 { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
|
||||||
|
import type { MessageChannel } from "../utils/channel/message-channel-injection-token";
|
||||||
|
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
|
||||||
|
|
||||||
|
export type ClusterFrameNavigationChannel = MessageChannel<string>;
|
||||||
|
|
||||||
|
const clusterFrameNavigationChannelInjectable = getInjectable({
|
||||||
|
id: "cluster-frame-navigation-channel",
|
||||||
|
|
||||||
|
instantiate: (): ClusterFrameNavigationChannel => ({
|
||||||
|
id: IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER,
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: messageChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clusterFrameNavigationChannelInjectable;
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import { createChannel } from "../ipc-channel/create-channel/create-channel";
|
|
||||||
import { IpcRendererNavigationEvents } from "../../renderer/navigation/events";
|
|
||||||
|
|
||||||
export const appNavigationIpcChannel = createChannel<string>(IpcRendererNavigationEvents.NAVIGATE_IN_APP);
|
|
||||||
export const clusterFrameNavigationIpcChannel = createChannel<string>(IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER);
|
|
||||||
@ -3,12 +3,11 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import packageInfo from "../../../../package.json";
|
import packageJsonInjectable from "../../vars/package-json.injectable";
|
||||||
|
|
||||||
const appVersionInjectable = getInjectable({
|
const appVersionInjectable = getInjectable({
|
||||||
id: "app-version",
|
id: "app-version",
|
||||||
instantiate: () => packageInfo.version,
|
instantiate: (di) => di.inject(packageJsonInjectable).version,
|
||||||
causesSideEffects: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default appVersionInjectable;
|
export default appVersionInjectable;
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import type { Channel } from "../channel";
|
|
||||||
|
|
||||||
export const createChannel = <Message>(name: string): Channel<Message> => ({
|
|
||||||
name,
|
|
||||||
_template: null as never,
|
|
||||||
});
|
|
||||||
@ -5,5 +5,4 @@
|
|||||||
|
|
||||||
export * from "./ipc";
|
export * from "./ipc";
|
||||||
export * from "./invalid-kubeconfig";
|
export * from "./invalid-kubeconfig";
|
||||||
export * from "./update-available";
|
|
||||||
export * from "./type-enforced-ipc";
|
export * from "./type-enforced-ipc";
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import type { UpdateInfo } from "electron-updater";
|
|
||||||
|
|
||||||
export const UpdateAvailableChannel = "update-available";
|
|
||||||
export const AutoUpdateChecking = "auto-update:checking";
|
|
||||||
export const AutoUpdateNoUpdateAvailable = "auto-update:no-update";
|
|
||||||
export const AutoUpdateLogPrefix = "[UPDATE-CHECKER]";
|
|
||||||
|
|
||||||
export type UpdateAvailableFromMain = [backChannel: string, updateInfo: UpdateInfo];
|
|
||||||
|
|
||||||
export function areArgsUpdateAvailableFromMain(args: unknown[]): args is UpdateAvailableFromMain {
|
|
||||||
if (args.length !== 2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof args[0] !== "string") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof args[1] !== "object" || args[1] === null) {
|
|
||||||
// TODO: improve this checking
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type BackchannelArg = {
|
|
||||||
doUpdate: false;
|
|
||||||
} | {
|
|
||||||
doUpdate: true;
|
|
||||||
now: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type UpdateAvailableToBackchannel = [updateDecision: BackchannelArg];
|
|
||||||
|
|
||||||
export function areArgsUpdateAvailableToBackchannel(args: unknown[]): args is UpdateAvailableToBackchannel {
|
|
||||||
if (args.length !== 1) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof args[0] !== "object" || args[0] === null) {
|
|
||||||
// TODO: improve this checking
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { MessageChannel } from "../utils/channel/message-channel-injection-token";
|
||||||
|
import { messageChannelInjectionToken } from "../utils/channel/message-channel-injection-token";
|
||||||
|
|
||||||
|
export type RootFrameRenderedChannel = MessageChannel;
|
||||||
|
|
||||||
|
const rootFrameRenderedChannelInjectable = getInjectable({
|
||||||
|
id: "root-frame-rendered-channel",
|
||||||
|
|
||||||
|
instantiate: (): RootFrameRenderedChannel => ({
|
||||||
|
id: "root-frame-rendered",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: messageChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default rootFrameRenderedChannelInjectable;
|
||||||
@ -6,14 +6,11 @@
|
|||||||
import moment from "moment-timezone";
|
import moment from "moment-timezone";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import os from "os";
|
import os from "os";
|
||||||
import { getAppVersion } from "../utils";
|
|
||||||
import type { editor } from "monaco-editor";
|
import type { editor } from "monaco-editor";
|
||||||
import merge from "lodash/merge";
|
import merge from "lodash/merge";
|
||||||
import { SemVer } from "semver";
|
|
||||||
import { defaultThemeId, defaultEditorFontFamily, defaultFontSize, defaultTerminalFontFamily } from "../vars";
|
import { defaultThemeId, defaultEditorFontFamily, defaultFontSize, defaultTerminalFontFamily } from "../vars";
|
||||||
import type { ObservableMap } from "mobx";
|
import type { ObservableMap } from "mobx";
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { readonly } from "../utils/readonly";
|
|
||||||
|
|
||||||
export interface KubeconfigSyncEntry extends KubeconfigSyncValue {
|
export interface KubeconfigSyncEntry extends KubeconfigSyncValue {
|
||||||
filePath: string;
|
filePath: string;
|
||||||
@ -296,38 +293,6 @@ const terminalConfig: PreferenceDescription<TerminalConfig, TerminalConfig> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface UpdateChannelInfo {
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const updateChannels = readonly(new Map<string, UpdateChannelInfo>([
|
|
||||||
["latest", {
|
|
||||||
label: "Stable",
|
|
||||||
}],
|
|
||||||
["beta", {
|
|
||||||
label: "Beta",
|
|
||||||
}],
|
|
||||||
["alpha", {
|
|
||||||
label: "Alpha",
|
|
||||||
}],
|
|
||||||
]));
|
|
||||||
export const defaultUpdateChannel = new SemVer(getAppVersion()).prerelease[0]?.toString() || "latest";
|
|
||||||
|
|
||||||
const updateChannel: PreferenceDescription<string> = {
|
|
||||||
fromStore(val) {
|
|
||||||
return !val || !updateChannels.has(val)
|
|
||||||
? defaultUpdateChannel
|
|
||||||
: val;
|
|
||||||
},
|
|
||||||
toStore(val) {
|
|
||||||
if (!updateChannels.has(val) || val === defaultUpdateChannel) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return val;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ExtensionRegistryLocation = "default" | "npmrc" | "custom";
|
export type ExtensionRegistryLocation = "default" | "npmrc" | "custom";
|
||||||
|
|
||||||
export type ExtensionRegistry = {
|
export type ExtensionRegistry = {
|
||||||
@ -365,7 +330,7 @@ export type UserStoreFlatModel = {
|
|||||||
|
|
||||||
export type UserPreferencesModel = {
|
export type UserPreferencesModel = {
|
||||||
[field in keyof typeof DESCRIPTORS]: PreferencesModelType<field>;
|
[field in keyof typeof DESCRIPTORS]: PreferencesModelType<field>;
|
||||||
};
|
} & { updateChannel: string };
|
||||||
|
|
||||||
export const DESCRIPTORS = {
|
export const DESCRIPTORS = {
|
||||||
httpsProxy,
|
httpsProxy,
|
||||||
@ -385,6 +350,5 @@ export const DESCRIPTORS = {
|
|||||||
editorConfiguration,
|
editorConfiguration,
|
||||||
terminalCopyOnSelect,
|
terminalCopyOnSelect,
|
||||||
terminalConfig,
|
terminalConfig,
|
||||||
updateChannel,
|
|
||||||
extensionRegistryUrl,
|
extensionRegistryUrl,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable";
|
|||||||
import { ipcMain } from "electron";
|
import { ipcMain } from "electron";
|
||||||
import userStoreFileNameMigrationInjectable from "./file-name-migration.injectable";
|
import userStoreFileNameMigrationInjectable from "./file-name-migration.injectable";
|
||||||
import { UserStore } from "./user-store";
|
import { UserStore } from "./user-store";
|
||||||
|
import selectedUpdateChannelInjectable from "../application-update/selected-update-channel/selected-update-channel.injectable";
|
||||||
|
|
||||||
const userStoreInjectable = getInjectable({
|
const userStoreInjectable = getInjectable({
|
||||||
id: "user-store",
|
id: "user-store",
|
||||||
@ -17,7 +18,9 @@ const userStoreInjectable = getInjectable({
|
|||||||
di.inject(userStoreFileNameMigrationInjectable);
|
di.inject(userStoreFileNameMigrationInjectable);
|
||||||
}
|
}
|
||||||
|
|
||||||
return UserStore.createInstance();
|
return UserStore.createInstance({
|
||||||
|
selectedUpdateChannel: di.inject(selectedUpdateChannelInjectable),
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
causesSideEffects: true,
|
causesSideEffects: true,
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import semver, { SemVer } from "semver";
|
import semver from "semver";
|
||||||
import { action, computed, observable, reaction, makeObservable, isObservableArray, isObservableSet, isObservableMap } from "mobx";
|
import { action, computed, observable, reaction, makeObservable, isObservableArray, isObservableSet, isObservableMap } from "mobx";
|
||||||
import { BaseStore } from "../base-store";
|
import { BaseStore } from "../base-store";
|
||||||
import migrations from "../../migrations/user-store";
|
import migrations from "../../migrations/user-store";
|
||||||
@ -15,15 +15,22 @@ import { getOrInsertSet, toggle, toJS, object } from "../../renderer/utils";
|
|||||||
import { DESCRIPTORS } from "./preferences-helpers";
|
import { DESCRIPTORS } from "./preferences-helpers";
|
||||||
import type { UserPreferencesModel, StoreType } from "./preferences-helpers";
|
import type { UserPreferencesModel, StoreType } from "./preferences-helpers";
|
||||||
import logger from "../../main/logger";
|
import logger from "../../main/logger";
|
||||||
|
import type { SelectedUpdateChannel } from "../application-update/selected-update-channel/selected-update-channel.injectable";
|
||||||
|
import type { UpdateChannelId } from "../application-update/update-channels";
|
||||||
|
|
||||||
export interface UserStoreModel {
|
export interface UserStoreModel {
|
||||||
lastSeenAppVersion: string;
|
lastSeenAppVersion: string;
|
||||||
preferences: UserPreferencesModel;
|
preferences: UserPreferencesModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Dependencies {
|
||||||
|
selectedUpdateChannel: SelectedUpdateChannel;
|
||||||
|
}
|
||||||
|
|
||||||
export class UserStore extends BaseStore<UserStoreModel> /* implements UserStoreFlatModel (when strict null is enabled) */ {
|
export class UserStore extends BaseStore<UserStoreModel> /* implements UserStoreFlatModel (when strict null is enabled) */ {
|
||||||
readonly displayName = "UserStore";
|
readonly displayName = "UserStore";
|
||||||
constructor() {
|
|
||||||
|
constructor(private readonly dependencies: Dependencies) {
|
||||||
super({
|
super({
|
||||||
configName: "lens-user-store",
|
configName: "lens-user-store",
|
||||||
migrations,
|
migrations,
|
||||||
@ -63,7 +70,6 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
|
|||||||
@observable kubectlBinariesPath!: StoreType<typeof DESCRIPTORS["kubectlBinariesPath"]>;
|
@observable kubectlBinariesPath!: StoreType<typeof DESCRIPTORS["kubectlBinariesPath"]>;
|
||||||
@observable terminalCopyOnSelect!: StoreType<typeof DESCRIPTORS["terminalCopyOnSelect"]>;
|
@observable terminalCopyOnSelect!: StoreType<typeof DESCRIPTORS["terminalCopyOnSelect"]>;
|
||||||
@observable terminalConfig!: StoreType<typeof DESCRIPTORS["terminalConfig"]>;
|
@observable terminalConfig!: StoreType<typeof DESCRIPTORS["terminalConfig"]>;
|
||||||
@observable updateChannel!: StoreType<typeof DESCRIPTORS["updateChannel"]>;
|
|
||||||
@observable extensionRegistryUrl!: StoreType<typeof DESCRIPTORS["extensionRegistryUrl"]>;
|
@observable extensionRegistryUrl!: StoreType<typeof DESCRIPTORS["extensionRegistryUrl"]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,10 +106,6 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
|
|||||||
return this.shell || process.env.SHELL || process.env.PTYSHELL;
|
return this.shell || process.env.SHELL || process.env.PTYSHELL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@computed get isAllowedToDowngrade() {
|
|
||||||
return new SemVer(getAppVersion()).prerelease[0] !== this.updateChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
startMainReactions() {
|
startMainReactions() {
|
||||||
// open at system start-up
|
// open at system start-up
|
||||||
reaction(() => this.openAtLogin, openAtLogin => {
|
reaction(() => this.openAtLogin, openAtLogin => {
|
||||||
@ -175,6 +177,11 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
|
|||||||
this[key] = newVal;
|
this[key] = newVal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Switch to action-based saving instead saving stores by reaction
|
||||||
|
if (preferences?.updateChannel) {
|
||||||
|
this.dependencies.selectedUpdateChannel.setValue(preferences?.updateChannel as UpdateChannelId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON(): UserStoreModel {
|
toJSON(): UserStoreModel {
|
||||||
@ -185,7 +192,12 @@ export class UserStore extends BaseStore<UserStoreModel> /* implements UserStore
|
|||||||
|
|
||||||
return toJS({
|
return toJS({
|
||||||
lastSeenAppVersion: this.lastSeenAppVersion,
|
lastSeenAppVersion: this.lastSeenAppVersion,
|
||||||
preferences,
|
|
||||||
|
preferences: {
|
||||||
|
...preferences,
|
||||||
|
|
||||||
|
updateChannel: this.dependencies.selectedUpdateChannel.value.get().id,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/common/utils/channel/channel-injection-token.ts
Normal file
12
src/common/utils/channel/channel-injection-token.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
export interface Channel<MessageTemplate = void, ReturnTemplate = void> {
|
||||||
|
id: string;
|
||||||
|
_messageTemplate?: MessageTemplate;
|
||||||
|
_returnTemplate?: ReturnTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
273
src/common/utils/channel/channel.test.ts
Normal file
273
src/common/utils/channel/channel.test.ts
Normal file
@ -0,0 +1,273 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { DiContainer } from "@ogre-tools/injectable";
|
||||||
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
|
import type { LensWindow } from "../../../main/start-main-application/lens-window/application-window/lens-window-injection-token";
|
||||||
|
import { lensWindowInjectionToken } from "../../../main/start-main-application/lens-window/application-window/lens-window-injection-token";
|
||||||
|
import type { MessageToChannel } from "./message-to-channel-injection-token";
|
||||||
|
import { messageToChannelInjectionToken } from "./message-to-channel-injection-token";
|
||||||
|
import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import createLensWindowInjectable from "../../../main/start-main-application/lens-window/application-window/create-lens-window.injectable";
|
||||||
|
import closeAllWindowsInjectable from "../../../main/start-main-application/lens-window/hide-all-windows/close-all-windows.injectable";
|
||||||
|
import { messageChannelListenerInjectionToken } from "./message-channel-listener-injection-token";
|
||||||
|
import type { MessageChannel } from "./message-channel-injection-token";
|
||||||
|
import type { RequestFromChannel } from "./request-from-channel-injection-token";
|
||||||
|
import { requestFromChannelInjectionToken } from "./request-from-channel-injection-token";
|
||||||
|
import type { RequestChannel } from "./request-channel-injection-token";
|
||||||
|
import { requestChannelListenerInjectionToken } from "./request-channel-listener-injection-token";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import { getPromiseStatus } from "../../test-utils/get-promise-status";
|
||||||
|
|
||||||
|
type TestMessageChannel = MessageChannel<string>;
|
||||||
|
type TestRequestChannel = RequestChannel<string, string>;
|
||||||
|
|
||||||
|
describe("channel", () => {
|
||||||
|
describe("messaging from main to renderer, given listener for channel in a window and application has started", () => {
|
||||||
|
let testMessageChannel: TestMessageChannel;
|
||||||
|
let messageListenerInWindowMock: jest.Mock;
|
||||||
|
let mainDi: DiContainer;
|
||||||
|
let messageToChannel: MessageToChannel;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
mainDi = applicationBuilder.dis.mainDi;
|
||||||
|
const rendererDi = applicationBuilder.dis.rendererDi;
|
||||||
|
|
||||||
|
messageListenerInWindowMock = jest.fn();
|
||||||
|
|
||||||
|
const testChannelListenerInTestWindowInjectable = getInjectable({
|
||||||
|
id: "test-channel-listener-in-test-window",
|
||||||
|
|
||||||
|
instantiate: (di) => ({
|
||||||
|
channel: di.inject(testMessageChannelInjectable),
|
||||||
|
|
||||||
|
handler: messageListenerInWindowMock,
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: messageChannelListenerInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
rendererDi.register(testChannelListenerInTestWindowInjectable);
|
||||||
|
|
||||||
|
// Notice how test channel has presence in both DIs, being from common
|
||||||
|
mainDi.register(testMessageChannelInjectable);
|
||||||
|
rendererDi.register(testMessageChannelInjectable);
|
||||||
|
|
||||||
|
testMessageChannel = mainDi.inject(testMessageChannelInjectable);
|
||||||
|
|
||||||
|
messageToChannel = mainDi.inject(
|
||||||
|
messageToChannelInjectionToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
|
||||||
|
const closeAllWindows = mainDi.inject(closeAllWindowsInjectable);
|
||||||
|
|
||||||
|
closeAllWindows();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given window is shown", () => {
|
||||||
|
let someWindowFake: LensWindow;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
someWindowFake = createTestWindow(mainDi, "some-window");
|
||||||
|
|
||||||
|
await someWindowFake.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when sending message, triggers listener in window", () => {
|
||||||
|
messageToChannel(testMessageChannel, "some-message");
|
||||||
|
|
||||||
|
expect(messageListenerInWindowMock).toHaveBeenCalledWith("some-message");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given window is hidden, when sending message, does not trigger listener in window", () => {
|
||||||
|
someWindowFake.close();
|
||||||
|
|
||||||
|
messageToChannel(testMessageChannel, "some-message");
|
||||||
|
|
||||||
|
expect(messageListenerInWindowMock).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given multiple shown windows, when sending message, triggers listeners in all windows", async () => {
|
||||||
|
const someWindowFake = createTestWindow(mainDi, "some-window");
|
||||||
|
const someOtherWindowFake = createTestWindow(mainDi, "some-other-window");
|
||||||
|
|
||||||
|
await someWindowFake.show();
|
||||||
|
await someOtherWindowFake.show();
|
||||||
|
|
||||||
|
messageToChannel(testMessageChannel, "some-message");
|
||||||
|
|
||||||
|
expect(messageListenerInWindowMock.mock.calls).toEqual([
|
||||||
|
["some-message"],
|
||||||
|
["some-message"],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("messaging from renderer to main, given listener for channel in a main and application has started", () => {
|
||||||
|
let testMessageChannel: TestMessageChannel;
|
||||||
|
let messageListenerInMainMock: jest.Mock;
|
||||||
|
let rendererDi: DiContainer;
|
||||||
|
let mainDi: DiContainer;
|
||||||
|
let messageToChannel: MessageToChannel;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
mainDi = applicationBuilder.dis.mainDi;
|
||||||
|
rendererDi = applicationBuilder.dis.rendererDi;
|
||||||
|
|
||||||
|
messageListenerInMainMock = jest.fn();
|
||||||
|
|
||||||
|
const testChannelListenerInMainInjectable = getInjectable({
|
||||||
|
id: "test-channel-listener-in-main",
|
||||||
|
|
||||||
|
instantiate: (di) => ({
|
||||||
|
channel: di.inject(testMessageChannelInjectable),
|
||||||
|
|
||||||
|
handler: messageListenerInMainMock,
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: messageChannelListenerInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
mainDi.register(testChannelListenerInMainInjectable);
|
||||||
|
|
||||||
|
// Notice how test channel has presence in both DIs, being from common
|
||||||
|
mainDi.register(testMessageChannelInjectable);
|
||||||
|
rendererDi.register(testMessageChannelInjectable);
|
||||||
|
|
||||||
|
testMessageChannel = rendererDi.inject(testMessageChannelInjectable);
|
||||||
|
|
||||||
|
messageToChannel = rendererDi.inject(
|
||||||
|
messageToChannelInjectionToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when sending message, triggers listener in main", () => {
|
||||||
|
messageToChannel(testMessageChannel, "some-message");
|
||||||
|
|
||||||
|
expect(messageListenerInMainMock).toHaveBeenCalledWith("some-message");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("requesting from main in renderer, given listener for channel in a main and application has started", () => {
|
||||||
|
let testRequestChannel: TestRequestChannel;
|
||||||
|
let requestListenerInMainMock: AsyncFnMock<(arg: string) => string>;
|
||||||
|
let rendererDi: DiContainer;
|
||||||
|
let mainDi: DiContainer;
|
||||||
|
let requestFromChannel: RequestFromChannel;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
mainDi = applicationBuilder.dis.mainDi;
|
||||||
|
rendererDi = applicationBuilder.dis.rendererDi;
|
||||||
|
|
||||||
|
requestListenerInMainMock = asyncFn();
|
||||||
|
|
||||||
|
const testChannelListenerInMainInjectable = getInjectable({
|
||||||
|
id: "test-channel-listener-in-main",
|
||||||
|
|
||||||
|
instantiate: (di) => ({
|
||||||
|
channel: di.inject(testRequestChannelInjectable),
|
||||||
|
|
||||||
|
handler: requestListenerInMainMock,
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: requestChannelListenerInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
mainDi.register(testChannelListenerInMainInjectable);
|
||||||
|
|
||||||
|
// Notice how test channel has presence in both DIs, being from common
|
||||||
|
mainDi.register(testRequestChannelInjectable);
|
||||||
|
rendererDi.register(testRequestChannelInjectable);
|
||||||
|
|
||||||
|
testRequestChannel = rendererDi.inject(testRequestChannelInjectable);
|
||||||
|
|
||||||
|
requestFromChannel = rendererDi.inject(
|
||||||
|
requestFromChannelInjectionToken,
|
||||||
|
);
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when requesting from channel", () => {
|
||||||
|
let actualPromise: Promise<string>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
actualPromise = requestFromChannel(testRequestChannel, "some-request");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("triggers listener in main", () => {
|
||||||
|
expect(requestListenerInMainMock).toHaveBeenCalledWith("some-request");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not resolve yet", async () => {
|
||||||
|
const promiseStatus = await getPromiseStatus(actualPromise);
|
||||||
|
|
||||||
|
expect(promiseStatus.fulfilled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when main resolves with response, resolves with response", async () => {
|
||||||
|
await requestListenerInMainMock.resolve("some-response");
|
||||||
|
|
||||||
|
const actual = await actualPromise;
|
||||||
|
|
||||||
|
expect(actual).toBe("some-response");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const testMessageChannelInjectable = getInjectable({
|
||||||
|
id: "some-message-test-channel",
|
||||||
|
|
||||||
|
instantiate: (): TestMessageChannel => ({
|
||||||
|
id: "some-message-channel-id",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const testRequestChannelInjectable = getInjectable({
|
||||||
|
id: "some-request-test-channel",
|
||||||
|
|
||||||
|
instantiate: (): TestRequestChannel => ({
|
||||||
|
id: "some-request-channel-id",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const createTestWindow = (di: DiContainer, id: string) => {
|
||||||
|
const testWindowInjectable = getInjectable({
|
||||||
|
id,
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const createLensWindow = di.inject(createLensWindowInjectable);
|
||||||
|
|
||||||
|
return createLensWindow({
|
||||||
|
id,
|
||||||
|
title: "Some test window",
|
||||||
|
defaultHeight: 42,
|
||||||
|
defaultWidth: 42,
|
||||||
|
getContentSource: () => ({ url: "some-content-url" }),
|
||||||
|
resizable: true,
|
||||||
|
windowFrameUtilitiesAreShown: false,
|
||||||
|
centered: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: lensWindowInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
di.register(testWindowInjectable);
|
||||||
|
|
||||||
|
return di.inject(testWindowInjectable);
|
||||||
|
};
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* 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 { MessageChannel } from "./message-channel-injection-token";
|
||||||
|
import type { MessageChannelListener } from "./message-channel-listener-injection-token";
|
||||||
|
|
||||||
|
export type EnlistMessageChannelListener = <
|
||||||
|
TChannel extends MessageChannel<any>,
|
||||||
|
>(listener: MessageChannelListener<TChannel>) => () => void;
|
||||||
|
|
||||||
|
export const enlistMessageChannelListenerInjectionToken =
|
||||||
|
getInjectionToken<EnlistMessageChannelListener>({
|
||||||
|
id: "enlist-message-channel-listener",
|
||||||
|
});
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* 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 { RequestChannel } from "./request-channel-injection-token";
|
||||||
|
import type { RequestChannelListener } from "./request-channel-listener-injection-token";
|
||||||
|
|
||||||
|
export type EnlistRequestChannelListener = <
|
||||||
|
TChannel extends RequestChannel<any, any>,
|
||||||
|
>(listener: RequestChannelListener<TChannel>) => () => void;
|
||||||
|
|
||||||
|
export const enlistRequestChannelListenerInjectionToken =
|
||||||
|
getInjectionToken<EnlistRequestChannelListener>({
|
||||||
|
id: "enlist-request-channel-listener",
|
||||||
|
});
|
||||||
32
src/common/utils/channel/listening-of-channels.injectable.ts
Normal file
32
src/common/utils/channel/listening-of-channels.injectable.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* 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 { getStartableStoppable } from "../get-startable-stoppable";
|
||||||
|
import { disposer } from "../index";
|
||||||
|
import { messageChannelListenerInjectionToken } from "./message-channel-listener-injection-token";
|
||||||
|
import { requestChannelListenerInjectionToken } from "./request-channel-listener-injection-token";
|
||||||
|
import { enlistMessageChannelListenerInjectionToken } from "./enlist-message-channel-listener-injection-token";
|
||||||
|
import { enlistRequestChannelListenerInjectionToken } from "./enlist-request-channel-listener-injection-token";
|
||||||
|
|
||||||
|
const listeningOfChannelsInjectable = getInjectable({
|
||||||
|
id: "listening-of-channels",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const enlistMessageChannelListener = di.inject(enlistMessageChannelListenerInjectionToken);
|
||||||
|
const enlistRequestChannelListener = di.inject(enlistRequestChannelListenerInjectionToken);
|
||||||
|
const messageChannelListeners = di.injectMany(messageChannelListenerInjectionToken);
|
||||||
|
const requestChannelListeners = di.injectMany(requestChannelListenerInjectionToken);
|
||||||
|
|
||||||
|
return getStartableStoppable("listening-of-channels", () => {
|
||||||
|
const messageChannelDisposers = messageChannelListeners.map(enlistMessageChannelListener);
|
||||||
|
const requestChannelDisposers = requestChannelListeners.map(enlistRequestChannelListener);
|
||||||
|
|
||||||
|
return disposer(...messageChannelDisposers, ...requestChannelDisposers);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export default listeningOfChannelsInjectable;
|
||||||
16
src/common/utils/channel/message-channel-injection-token.ts
Normal file
16
src/common/utils/channel/message-channel-injection-token.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* 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 { JsonValue } from "type-fest";
|
||||||
|
|
||||||
|
export interface MessageChannel<Message extends JsonValue | void = void> {
|
||||||
|
id: string;
|
||||||
|
_messageSignature?: Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const messageChannelInjectionToken = getInjectionToken<MessageChannel<any>>({
|
||||||
|
id: "message-channel",
|
||||||
|
});
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* 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 { SetRequired } from "type-fest";
|
||||||
|
import type { MessageChannel } from "./message-channel-injection-token";
|
||||||
|
|
||||||
|
export interface MessageChannelListener<TChannel extends MessageChannel<any>> {
|
||||||
|
channel: TChannel;
|
||||||
|
handler: (value: SetRequired<TChannel, "_messageSignature">["_messageSignature"]) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const messageChannelListenerInjectionToken = getInjectionToken<MessageChannelListener<MessageChannel<any>>>(
|
||||||
|
{
|
||||||
|
id: "message-channel-listener",
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
/**
|
||||||
|
* 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 { SetRequired } from "type-fest";
|
||||||
|
import type { MessageChannel } from "./message-channel-injection-token";
|
||||||
|
|
||||||
|
export interface MessageToChannel {
|
||||||
|
<TChannel extends MessageChannel<TMessage>, TMessage extends void>(
|
||||||
|
channel: TChannel,
|
||||||
|
): void;
|
||||||
|
|
||||||
|
<TChannel extends MessageChannel<any>>(
|
||||||
|
channel: TChannel,
|
||||||
|
message: SetRequired<TChannel, "_messageSignature">["_messageSignature"],
|
||||||
|
): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const messageToChannelInjectionToken =
|
||||||
|
getInjectionToken<MessageToChannel>({
|
||||||
|
id: "message-to-message-channel",
|
||||||
|
});
|
||||||
20
src/common/utils/channel/request-channel-injection-token.ts
Normal file
20
src/common/utils/channel/request-channel-injection-token.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 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 { JsonValue } from "type-fest";
|
||||||
|
|
||||||
|
export interface RequestChannel<
|
||||||
|
Request extends JsonValue | void = void,
|
||||||
|
Response extends JsonValue | void = void,
|
||||||
|
> {
|
||||||
|
id: string;
|
||||||
|
_requestSignature?: Request;
|
||||||
|
_responseSignature?: Response;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const requestChannelInjectionToken = getInjectionToken<RequestChannel<any, any>>({
|
||||||
|
id: "request-channel",
|
||||||
|
});
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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 { SetRequired } from "type-fest";
|
||||||
|
import type { RequestChannel } from "./request-channel-injection-token";
|
||||||
|
|
||||||
|
export interface RequestChannelListener<TChannel extends RequestChannel<any, any>> {
|
||||||
|
channel: TChannel;
|
||||||
|
|
||||||
|
handler: (
|
||||||
|
request: SetRequired<TChannel, "_requestSignature">["_requestSignature"]
|
||||||
|
) =>
|
||||||
|
| SetRequired<TChannel, "_responseSignature">["_responseSignature"]
|
||||||
|
| Promise<
|
||||||
|
SetRequired<TChannel, "_responseSignature">["_responseSignature"]
|
||||||
|
>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const requestChannelListenerInjectionToken = getInjectionToken<RequestChannelListener<RequestChannel<any, any>>>(
|
||||||
|
{
|
||||||
|
id: "request-channel-listener",
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { SetRequired } from "type-fest";
|
||||||
|
import type { RequestChannel } from "./request-channel-injection-token";
|
||||||
|
|
||||||
|
export type RequestFromChannel = <
|
||||||
|
TChannel extends RequestChannel<any, any>,
|
||||||
|
>(
|
||||||
|
channel: TChannel,
|
||||||
|
...request: TChannel["_requestSignature"] extends void
|
||||||
|
? []
|
||||||
|
: [TChannel["_requestSignature"]]
|
||||||
|
) => Promise<SetRequired<TChannel, "_responseSignature">["_responseSignature"]>;
|
||||||
|
|
||||||
|
export const requestFromChannelInjectionToken =
|
||||||
|
getInjectionToken<RequestFromChannel>({
|
||||||
|
id: "request-from-request-channel",
|
||||||
|
});
|
||||||
14
src/common/utils/get-random-id.injectable.ts
Normal file
14
src/common/utils/get-random-id.injectable.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 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 { v4 as getRandomId } from "uuid";
|
||||||
|
|
||||||
|
const getRandomIdInjectable = getInjectable({
|
||||||
|
id: "get-random-id",
|
||||||
|
instantiate: () => getRandomId,
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default getRandomIdInjectable;
|
||||||
31
src/common/utils/is-promise/is-promise.test.ts
Normal file
31
src/common/utils/is-promise/is-promise.test.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { isPromise } from "./is-promise";
|
||||||
|
|
||||||
|
describe("isPromise", () => {
|
||||||
|
it("given promise, returns true", () => {
|
||||||
|
const actual = isPromise(new Promise(() => {}));
|
||||||
|
|
||||||
|
expect(actual).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given non-promise, returns false", () => {
|
||||||
|
const actual = isPromise({});
|
||||||
|
|
||||||
|
expect(actual).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given thenable, returns false", () => {
|
||||||
|
const actual = isPromise({ then: () => {} });
|
||||||
|
|
||||||
|
expect(actual).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given nothing, returns false", () => {
|
||||||
|
const actual = isPromise(undefined);
|
||||||
|
|
||||||
|
expect(actual).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -2,7 +2,6 @@
|
|||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
export interface Channel<TInstance> {
|
export function isPromise(reference: any): reference is Promise<any> {
|
||||||
name: string;
|
return reference?.constructor === Promise;
|
||||||
_template: TInstance;
|
|
||||||
}
|
}
|
||||||
41
src/common/utils/sync-box/create-sync-box.injectable.ts
Normal file
41
src/common/utils/sync-box/create-sync-box.injectable.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 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 { computed } from "mobx";
|
||||||
|
import syncBoxChannelInjectable from "./sync-box-channel.injectable";
|
||||||
|
import { messageToChannelInjectionToken } from "../channel/message-to-channel-injection-token";
|
||||||
|
import syncBoxStateInjectable from "./sync-box-state.injectable";
|
||||||
|
import type { SyncBox } from "./sync-box-injection-token";
|
||||||
|
|
||||||
|
const createSyncBoxInjectable = getInjectable({
|
||||||
|
id: "create-sync-box",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const syncBoxChannel = di.inject(syncBoxChannelInjectable);
|
||||||
|
const messageToChannel = di.inject(messageToChannelInjectionToken);
|
||||||
|
const getSyncBoxState = (id: string) => di.inject(syncBoxStateInjectable, id);
|
||||||
|
|
||||||
|
return <TData>(id: string, initialValue: TData): SyncBox<TData> => {
|
||||||
|
const state = getSyncBoxState(id);
|
||||||
|
|
||||||
|
state.set(initialValue);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
|
||||||
|
value: computed(() => state.get()),
|
||||||
|
|
||||||
|
set: (value) => {
|
||||||
|
state.set(value);
|
||||||
|
|
||||||
|
messageToChannel(syncBoxChannel, { id, value });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default createSyncBoxInjectable;
|
||||||
|
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* 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 { SyncBoxChannel } from "./sync-box-channel.injectable";
|
||||||
|
import syncBoxChannelInjectable from "./sync-box-channel.injectable";
|
||||||
|
import syncBoxStateInjectable from "./sync-box-state.injectable";
|
||||||
|
import type { MessageChannelListener } from "../channel/message-channel-listener-injection-token";
|
||||||
|
import { messageChannelListenerInjectionToken } from "../channel/message-channel-listener-injection-token";
|
||||||
|
|
||||||
|
const syncBoxChannelListenerInjectable = getInjectable({
|
||||||
|
id: "sync-box-channel-listener",
|
||||||
|
|
||||||
|
instantiate: (di): MessageChannelListener<SyncBoxChannel> => {
|
||||||
|
const getSyncBoxState = (id: string) => di.inject(syncBoxStateInjectable, id);
|
||||||
|
const channel = di.inject(syncBoxChannelInjectable);
|
||||||
|
|
||||||
|
return {
|
||||||
|
channel,
|
||||||
|
|
||||||
|
handler: ({ id, value }) => {
|
||||||
|
const target = getSyncBoxState(id);
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
target.set(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
injectionToken: messageChannelListenerInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default syncBoxChannelListenerInjectable;
|
||||||
21
src/common/utils/sync-box/sync-box-channel.injectable.ts
Normal file
21
src/common/utils/sync-box/sync-box-channel.injectable.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* 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 { MessageChannel } from "../channel/message-channel-injection-token";
|
||||||
|
import { messageChannelInjectionToken } from "../channel/message-channel-injection-token";
|
||||||
|
|
||||||
|
export type SyncBoxChannel = MessageChannel<{ id: string; value: any }>;
|
||||||
|
|
||||||
|
const syncBoxChannelInjectable = getInjectable({
|
||||||
|
id: "sync-box-channel",
|
||||||
|
|
||||||
|
instantiate: (): SyncBoxChannel => ({
|
||||||
|
id: "sync-box-channel",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: messageChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default syncBoxChannelInjectable;
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 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 { RequestChannel } from "../channel/request-channel-injection-token";
|
||||||
|
import { requestChannelInjectionToken } from "../channel/request-channel-injection-token";
|
||||||
|
|
||||||
|
export type SyncBoxInitialValueChannel = RequestChannel<
|
||||||
|
void,
|
||||||
|
{ id: string; value: any }[]
|
||||||
|
>;
|
||||||
|
|
||||||
|
const syncBoxInitialValueChannelInjectable = getInjectable({
|
||||||
|
id: "sync-box-initial-value-channel",
|
||||||
|
|
||||||
|
instantiate: (): SyncBoxInitialValueChannel => ({
|
||||||
|
id: "sync-box-initial-value-channel",
|
||||||
|
}),
|
||||||
|
|
||||||
|
injectionToken: requestChannelInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default syncBoxInitialValueChannelInjectable;
|
||||||
17
src/common/utils/sync-box/sync-box-injection-token.ts
Normal file
17
src/common/utils/sync-box/sync-box-injection-token.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* 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 { IComputedValue } from "mobx";
|
||||||
|
import type { JsonValue } from "type-fest";
|
||||||
|
|
||||||
|
export interface SyncBox<TValue extends JsonValue> {
|
||||||
|
id: string;
|
||||||
|
value: IComputedValue<TValue>;
|
||||||
|
set: (value: TValue) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const syncBoxInjectionToken = getInjectionToken<SyncBox<any>>({
|
||||||
|
id: "sync-box",
|
||||||
|
});
|
||||||
18
src/common/utils/sync-box/sync-box-state.injectable.ts
Normal file
18
src/common/utils/sync-box/sync-box-state.injectable.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
|
||||||
|
import { observable } from "mobx";
|
||||||
|
|
||||||
|
const syncBoxStateInjectable = getInjectable({
|
||||||
|
id: "sync-box-state",
|
||||||
|
|
||||||
|
instantiate: () => observable.box(),
|
||||||
|
|
||||||
|
lifecycle: lifecycleEnum.keyedSingleton({
|
||||||
|
getInstanceKey: (di, id: string) => id,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default syncBoxStateInjectable;
|
||||||
179
src/common/utils/sync-box/sync-box.test.ts
Normal file
179
src/common/utils/sync-box/sync-box.test.ts
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/**
|
||||||
|
* 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 { observe, runInAction } from "mobx";
|
||||||
|
import type { ApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import { getApplicationBuilder } from "../../../renderer/components/test-utils/get-application-builder";
|
||||||
|
import createSyncBoxInjectable from "./create-sync-box.injectable";
|
||||||
|
import { flushPromises } from "../../test-utils/flush-promises";
|
||||||
|
import type { SyncBox } from "./sync-box-injection-token";
|
||||||
|
|
||||||
|
describe("sync-box", () => {
|
||||||
|
let applicationBuilder: ApplicationBuilder;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
applicationBuilder = getApplicationBuilder();
|
||||||
|
|
||||||
|
applicationBuilder.dis.mainDi.register(someInjectable);
|
||||||
|
applicationBuilder.dis.rendererDi.register(someInjectable);
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Separate starting for main application and starting of window in application builder
|
||||||
|
xdescribe("given application is started, when value is set in main", () => {
|
||||||
|
let valueInMain: string;
|
||||||
|
let syncBoxInMain: SyncBox<string>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
syncBoxInMain = applicationBuilder.dis.mainDi.inject(someInjectable);
|
||||||
|
|
||||||
|
// await applicationBuilder.start();
|
||||||
|
|
||||||
|
observe(syncBoxInMain.value, ({ newValue }) => {
|
||||||
|
valueInMain = newValue as string;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
syncBoxInMain.set("some-value-from-main");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("knows value in main", () => {
|
||||||
|
expect(valueInMain).toBe("some-value-from-main");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when window starts", () => {
|
||||||
|
let valueInRenderer: string;
|
||||||
|
let syncBoxInRenderer: SyncBox<string>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// applicationBuilder.renderWindow()
|
||||||
|
|
||||||
|
syncBoxInRenderer = applicationBuilder.dis.rendererDi.inject(someInjectable);
|
||||||
|
|
||||||
|
observe(syncBoxInRenderer.value, ({ newValue }) => {
|
||||||
|
valueInRenderer = newValue as string;
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not have the initial value yet", () => {
|
||||||
|
expect(valueInRenderer).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when getting initial value resolves", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await flushPromises();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has value in renderer", () => {
|
||||||
|
expect(valueInRenderer).toBe("some-value-from-main");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when value is set from renderer", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
runInAction(() => {
|
||||||
|
syncBoxInRenderer.set("some-value-from-renderer");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has value in main", () => {
|
||||||
|
expect(valueInMain).toBe("some-value-from-renderer");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has value in renderer", () => {
|
||||||
|
expect(valueInRenderer).toBe("some-value-from-renderer");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when value is set from renderer before getting initial value from main resolves", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
runInAction(() => {
|
||||||
|
syncBoxInRenderer.set("some-value-from-renderer");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has value in main", () => {
|
||||||
|
expect(valueInMain).toBe("some-value-from-renderer");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has value in renderer", () => {
|
||||||
|
expect(valueInRenderer).toBe("some-value-from-renderer");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when application starts with a window", () => {
|
||||||
|
let valueInRenderer: string;
|
||||||
|
let valueInMain: string;
|
||||||
|
let syncBoxInMain: SyncBox<string>;
|
||||||
|
let syncBoxInRenderer: SyncBox<string>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
syncBoxInMain = applicationBuilder.dis.mainDi.inject(someInjectable);
|
||||||
|
syncBoxInRenderer = applicationBuilder.dis.rendererDi.inject(someInjectable);
|
||||||
|
|
||||||
|
await applicationBuilder.render();
|
||||||
|
|
||||||
|
observe(syncBoxInRenderer.value, ({ newValue }) => {
|
||||||
|
valueInRenderer = newValue as string;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
observe(syncBoxInMain.value, ({ newValue }) => {
|
||||||
|
valueInMain = newValue as string;
|
||||||
|
}, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("knows initial value in main", () => {
|
||||||
|
expect(valueInMain).toBe("some-initial-value");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("knows initial value in renderer", () => {
|
||||||
|
expect(valueInRenderer).toBe("some-initial-value");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when value is set from main", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
runInAction(() => {
|
||||||
|
syncBoxInMain.set("some-value-from-main");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has value in main", () => {
|
||||||
|
expect(valueInMain).toBe("some-value-from-main");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has value in renderer", () => {
|
||||||
|
expect(valueInRenderer).toBe("some-value-from-main");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when value is set from renderer", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
runInAction(() => {
|
||||||
|
syncBoxInRenderer.set("some-value-from-renderer");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has value in main", () => {
|
||||||
|
expect(valueInMain).toBe("some-value-from-renderer");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("has value in renderer", () => {
|
||||||
|
expect(valueInRenderer).toBe("some-value-from-renderer");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const someInjectable = getInjectable({
|
||||||
|
id: "some-injectable",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const createSyncBox = di.inject(createSyncBoxInjectable);
|
||||||
|
|
||||||
|
return createSyncBox("some-sync-box", "some-initial-value");
|
||||||
|
},
|
||||||
|
});
|
||||||
15
src/common/utils/tentative-parse-json.ts
Normal file
15
src/common/utils/tentative-parse-json.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
import { defaultTo } from "lodash/fp";
|
||||||
|
import { withErrorSuppression } from "./with-error-suppression/with-error-suppression";
|
||||||
|
|
||||||
|
export const tentativeParseJson = (toBeParsed: any) => pipeline(
|
||||||
|
toBeParsed,
|
||||||
|
withErrorSuppression(JSON.parse),
|
||||||
|
defaultTo(toBeParsed),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
15
src/common/utils/tentative-stringify-json.ts
Normal file
15
src/common/utils/tentative-stringify-json.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
import { defaultTo } from "lodash/fp";
|
||||||
|
import { withErrorSuppression } from "./with-error-suppression/with-error-suppression";
|
||||||
|
|
||||||
|
export const tentativeStringifyJson = (toBeParsed: any) => pipeline(
|
||||||
|
toBeParsed,
|
||||||
|
withErrorSuppression(JSON.stringify),
|
||||||
|
defaultTo(toBeParsed),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* 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 loggerInjectable from "../../logger.injectable";
|
||||||
|
import { isPromise } from "../is-promise/is-promise";
|
||||||
|
|
||||||
|
export type WithErrorLoggingFor = (
|
||||||
|
getErrorMessage: (error: unknown) => string
|
||||||
|
) => <T extends (...args: any[]) => any>(
|
||||||
|
toBeDecorated: T
|
||||||
|
) => (...args: Parameters<T>) => ReturnType<T>;
|
||||||
|
|
||||||
|
const withErrorLoggingInjectable = getInjectable({
|
||||||
|
id: "with-error-logging",
|
||||||
|
|
||||||
|
instantiate: (di): WithErrorLoggingFor => {
|
||||||
|
const logger = di.inject(loggerInjectable);
|
||||||
|
|
||||||
|
return (getErrorMessage) =>
|
||||||
|
(toBeDecorated) =>
|
||||||
|
(...args) => {
|
||||||
|
try {
|
||||||
|
const returnValue = toBeDecorated(...args);
|
||||||
|
|
||||||
|
if (isPromise(returnValue)) {
|
||||||
|
returnValue.catch((e) => {
|
||||||
|
const errorMessage = getErrorMessage(e);
|
||||||
|
|
||||||
|
logger.error(errorMessage, e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
} catch (e) {
|
||||||
|
const errorMessage = getErrorMessage(e);
|
||||||
|
|
||||||
|
logger.error(errorMessage, e);
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default withErrorLoggingInjectable;
|
||||||
243
src/common/utils/with-error-logging/with-error-logging.test.ts
Normal file
243
src/common/utils/with-error-logging/with-error-logging.test.ts
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { getDiForUnitTesting } from "../../../main/getDiForUnitTesting";
|
||||||
|
import loggerInjectable from "../../logger.injectable";
|
||||||
|
import type { Logger } from "../../logger";
|
||||||
|
import withErrorLoggingInjectable from "./with-error-logging.injectable";
|
||||||
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import { getPromiseStatus } from "../../test-utils/get-promise-status";
|
||||||
|
|
||||||
|
describe("with-error-logging", () => {
|
||||||
|
describe("given decorated sync function", () => {
|
||||||
|
let loggerStub: Logger;
|
||||||
|
let toBeDecorated: jest.Mock<number | undefined, [string, string]>;
|
||||||
|
let decorated: (a: string, b: string) => number | undefined;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const di = getDiForUnitTesting();
|
||||||
|
|
||||||
|
loggerStub = {
|
||||||
|
error: jest.fn(),
|
||||||
|
} as unknown as Logger;
|
||||||
|
|
||||||
|
di.override(loggerInjectable, () => loggerStub);
|
||||||
|
|
||||||
|
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
|
||||||
|
|
||||||
|
toBeDecorated = jest.fn();
|
||||||
|
|
||||||
|
decorated = pipeline(
|
||||||
|
toBeDecorated,
|
||||||
|
withErrorLoggingFor((error: any) => `some-error-message-for-${error.message}`),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when function does not throw and returns value", () => {
|
||||||
|
let returnValue: number | undefined;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// eslint-disable-next-line unused-imports/no-unused-vars-ts
|
||||||
|
toBeDecorated.mockImplementation((_, __) => 42);
|
||||||
|
|
||||||
|
returnValue = decorated("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes arguments to decorated function", () => {
|
||||||
|
expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not log error", () => {
|
||||||
|
expect(loggerStub.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the value", () => {
|
||||||
|
expect(returnValue).toBe(42);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when function does not throw and returns no value", () => {
|
||||||
|
let returnValue: number | undefined;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// eslint-disable-next-line unused-imports/no-unused-vars-ts
|
||||||
|
toBeDecorated.mockImplementation((_, __) => undefined);
|
||||||
|
|
||||||
|
returnValue = decorated("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes arguments to decorated function", () => {
|
||||||
|
expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not log error", () => {
|
||||||
|
expect(loggerStub.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns nothing", () => {
|
||||||
|
expect(returnValue).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when function throws", () => {
|
||||||
|
let error: Error;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// eslint-disable-next-line unused-imports/no-unused-vars-ts
|
||||||
|
toBeDecorated.mockImplementation((_, __) => {
|
||||||
|
throw new Error("some-error");
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
decorated("some-parameter", "some-other-parameter");
|
||||||
|
} catch (e: any) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes arguments to decorated function", () => {
|
||||||
|
expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs the error", () => {
|
||||||
|
expect(loggerStub.error).toHaveBeenCalledWith("some-error-message-for-some-error", error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("throws", () => {
|
||||||
|
expect(error.message).toBe("some-error");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given decorated async function", () => {
|
||||||
|
let loggerStub: Logger;
|
||||||
|
let decorated: (a: string, b: string) => Promise<number | undefined>;
|
||||||
|
let toBeDecorated: AsyncFnMock<typeof decorated>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const di = getDiForUnitTesting();
|
||||||
|
|
||||||
|
loggerStub = {
|
||||||
|
error: jest.fn(),
|
||||||
|
} as unknown as Logger;
|
||||||
|
|
||||||
|
di.override(loggerInjectable, () => loggerStub);
|
||||||
|
|
||||||
|
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
|
||||||
|
|
||||||
|
toBeDecorated = asyncFn();
|
||||||
|
|
||||||
|
decorated = pipeline(
|
||||||
|
toBeDecorated,
|
||||||
|
|
||||||
|
withErrorLoggingFor(
|
||||||
|
(error: any) =>
|
||||||
|
`some-error-message-for-${error.message || error.someProperty}`,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when called", () => {
|
||||||
|
let returnValuePromise: Promise<number | undefined>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
returnValuePromise = decorated("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes arguments to decorated function", () => {
|
||||||
|
expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not log error yet", () => {
|
||||||
|
expect(loggerStub.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not resolve yet", async () => {
|
||||||
|
const promiseStatus = await getPromiseStatus(returnValuePromise);
|
||||||
|
|
||||||
|
expect(promiseStatus.fulfilled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when call rejects with error instance", () => {
|
||||||
|
let error: Error;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
try {
|
||||||
|
await toBeDecorated.reject(new Error("some-error"));
|
||||||
|
await returnValuePromise;
|
||||||
|
} catch (e) {
|
||||||
|
error = e as Error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs the error", () => {
|
||||||
|
expect(loggerStub.error).toHaveBeenCalledWith("some-error-message-for-some-error", error);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects", () => {
|
||||||
|
return expect(() => returnValuePromise).rejects.toThrow("some-error");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when call rejects with something else than error instance", () => {
|
||||||
|
let error: unknown;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
try {
|
||||||
|
await toBeDecorated.reject({ someProperty: "some-rejection" });
|
||||||
|
await returnValuePromise;
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs the rejection", () => {
|
||||||
|
expect(loggerStub.error).toHaveBeenCalledWith(
|
||||||
|
"some-error-message-for-some-rejection",
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("rejects", () => {
|
||||||
|
return expect(() => returnValuePromise).rejects.toEqual({ someProperty: "some-rejection" });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when call resolves with value", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await toBeDecorated.resolve(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not log error", () => {
|
||||||
|
expect(loggerStub.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves with the value", async () => {
|
||||||
|
const returnValue = await returnValuePromise;
|
||||||
|
|
||||||
|
expect(returnValue).toBe(42);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when call resolves without value", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await toBeDecorated.resolve(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not log error", () => {
|
||||||
|
expect(loggerStub.error).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("resolves without value", async () => {
|
||||||
|
const returnValue = await returnValuePromise;
|
||||||
|
|
||||||
|
expect(returnValue).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import { getPromiseStatus } from "../../test-utils/get-promise-status";
|
||||||
|
import { withErrorSuppression } from "./with-error-suppression";
|
||||||
|
|
||||||
|
describe("with-error-suppression", () => {
|
||||||
|
describe("given decorated sync function", () => {
|
||||||
|
let toBeDecorated: jest.Mock<void, [string, string]>;
|
||||||
|
let decorated: (a: string, b: string) => void;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
toBeDecorated = jest.fn();
|
||||||
|
|
||||||
|
decorated = withErrorSuppression(toBeDecorated);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when function does not throw", () => {
|
||||||
|
let returnValue: void;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
returnValue = decorated("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes arguments to decorated function", () => {
|
||||||
|
expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns nothing", () => {
|
||||||
|
expect(returnValue).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when function throws", () => {
|
||||||
|
let returnValue: void;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
// eslint-disable-next-line unused-imports/no-unused-vars-ts
|
||||||
|
toBeDecorated.mockImplementation((_, __) => {
|
||||||
|
throw new Error("some-error");
|
||||||
|
});
|
||||||
|
|
||||||
|
returnValue = decorated("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes arguments to decorated function", () => {
|
||||||
|
expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns nothing", () => {
|
||||||
|
expect(returnValue).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("given decorated async function", () => {
|
||||||
|
let decorated: (a: string, b: string) => Promise<number> | Promise<void>;
|
||||||
|
let toBeDecorated: AsyncFnMock<(a: string, b: string) => number>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
toBeDecorated = asyncFn();
|
||||||
|
|
||||||
|
decorated = withErrorSuppression(toBeDecorated);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when called", () => {
|
||||||
|
let returnValuePromise: Promise<number> | Promise<void>;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
returnValuePromise = decorated("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("passes arguments to decorated function", () => {
|
||||||
|
expect(toBeDecorated).toHaveBeenCalledWith("some-parameter", "some-other-parameter");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not resolve yet", async () => {
|
||||||
|
const promiseStatus = await getPromiseStatus(returnValuePromise);
|
||||||
|
|
||||||
|
expect(promiseStatus.fulfilled).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when call rejects, resolves with nothing", async () => {
|
||||||
|
await toBeDecorated.reject(new Error("some-error"));
|
||||||
|
|
||||||
|
const returnValue = await returnValuePromise;
|
||||||
|
|
||||||
|
expect(returnValue).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when call resolves, resolves with the value", async () => {
|
||||||
|
await toBeDecorated.resolve(42);
|
||||||
|
|
||||||
|
const returnValue = await returnValuePromise;
|
||||||
|
|
||||||
|
expect(returnValue).toBe(42);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import { noop } from "lodash/fp";
|
||||||
|
|
||||||
|
export function withErrorSuppression<TDecorated extends (...args: any[]) => Promise<any>>(toBeDecorated: TDecorated): (...args: Parameters<TDecorated>) => ReturnType<TDecorated> | Promise<void>;
|
||||||
|
export function withErrorSuppression<TDecorated extends (...args: any[]) => any>(toBeDecorated: TDecorated): (...args: Parameters<TDecorated>) => ReturnType<TDecorated> | void;
|
||||||
|
|
||||||
|
export function withErrorSuppression(toBeDecorated: any) {
|
||||||
|
return (...args: any[]) => {
|
||||||
|
try {
|
||||||
|
const returnValue = toBeDecorated(...args);
|
||||||
|
|
||||||
|
if (isPromise(returnValue)) {
|
||||||
|
return returnValue.catch(noop);
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
} catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPromise(reference: any): reference is Promise<any> {
|
||||||
|
return !!reference?.then;
|
||||||
|
}
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
/**
|
||||||
|
* 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 withErrorLoggingInjectable from "../with-error-logging/with-error-logging.injectable";
|
||||||
|
import { withErrorSuppression } from "../with-error-suppression/with-error-suppression";
|
||||||
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
|
|
||||||
|
const withOrphanPromiseInjectable = getInjectable({
|
||||||
|
id: "with-orphan-promise",
|
||||||
|
|
||||||
|
instantiate: (di) => {
|
||||||
|
const withErrorLoggingFor = di.inject(withErrorLoggingInjectable);
|
||||||
|
|
||||||
|
return <T extends (...args: any[]) => Promise<any>>(toBeDecorated: T) =>
|
||||||
|
(...args: Parameters<T>): void => {
|
||||||
|
const decorated = pipeline(
|
||||||
|
toBeDecorated,
|
||||||
|
withErrorLoggingFor(() => "Orphan promise rejection encountered"),
|
||||||
|
withErrorSuppression,
|
||||||
|
);
|
||||||
|
|
||||||
|
decorated(...args);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default withOrphanPromiseInjectable;
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) OpenLens Authors. All rights reserved.
|
||||||
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
|
*/
|
||||||
|
import type { AsyncFnMock } from "@async-fn/jest";
|
||||||
|
import asyncFn from "@async-fn/jest";
|
||||||
|
import { getDiForUnitTesting } from "../../../main/getDiForUnitTesting";
|
||||||
|
import loggerInjectable from "../../logger.injectable";
|
||||||
|
import type { Logger } from "../../logger";
|
||||||
|
import withOrphanPromiseInjectable from "./with-orphan-promise.injectable";
|
||||||
|
|
||||||
|
describe("with orphan promise, when called", () => {
|
||||||
|
let toBeDecorated: AsyncFnMock<(arg1: string, arg2: string) => Promise<string>>;
|
||||||
|
let actual: void;
|
||||||
|
let loggerStub: Logger;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const di = getDiForUnitTesting({ doGeneralOverrides: true });
|
||||||
|
|
||||||
|
loggerStub = { error: jest.fn() } as unknown as Logger;
|
||||||
|
|
||||||
|
di.override(loggerInjectable, () => loggerStub);
|
||||||
|
|
||||||
|
const withOrphanPromise = di.inject(withOrphanPromiseInjectable);
|
||||||
|
|
||||||
|
toBeDecorated = asyncFn();
|
||||||
|
|
||||||
|
const decorated = withOrphanPromise(toBeDecorated);
|
||||||
|
|
||||||
|
actual = decorated("some-argument", "some-other-argument");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("calls decorated with arguments", () => {
|
||||||
|
expect(toBeDecorated).toHaveBeenCalledWith("some-argument", "some-other-argument");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("given promise returned by decorated has not been fulfilled yet, already returns nothing", () => {
|
||||||
|
expect(actual).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("when decorated function resolves, nothing happens", async () => {
|
||||||
|
await toBeDecorated.resolve("irrelevant");
|
||||||
|
// Note: there is no expect, test is here only for documentation.
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("when decorated function rejects", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await toBeDecorated.reject("some-error");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("logs the rejection", () => {
|
||||||
|
expect(loggerStub.error).toHaveBeenCalledWith("Orphan promise rejection encountered", "some-error");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("nothing else happens", () => {
|
||||||
|
// Note: there is no expect, test is here only for documentation.
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -43,8 +43,6 @@ export const isProduction = process.env.NODE_ENV === "production";
|
|||||||
*/
|
*/
|
||||||
export const isDevelopment = !isTestEnv && !isProduction;
|
export const isDevelopment = !isTestEnv && !isProduction;
|
||||||
|
|
||||||
export const isPublishConfigured = Object.keys(packageInfo.build).includes("publish");
|
|
||||||
|
|
||||||
export const productName = packageInfo.productName;
|
export const productName = packageInfo.productName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
14
src/common/vars/package-json.injectable.ts
Normal file
14
src/common/vars/package-json.injectable.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 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 packageJson from "../../../package.json";
|
||||||
|
|
||||||
|
const packageJsonInjectable = getInjectable({
|
||||||
|
id: "package-json",
|
||||||
|
instantiate: () => packageJson,
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default packageJsonInjectable;
|
||||||
@ -46,7 +46,6 @@ import { mock } from "jest-mock-extended";
|
|||||||
import { waitUntilUsed } from "tcp-port-used";
|
import { waitUntilUsed } from "tcp-port-used";
|
||||||
import type { Readable } from "stream";
|
import type { Readable } from "stream";
|
||||||
import { EventEmitter } from "stream";
|
import { EventEmitter } from "stream";
|
||||||
import { UserStore } from "../../common/user-store";
|
|
||||||
import { Console } from "console";
|
import { Console } from "console";
|
||||||
import { stdout, stderr } from "process";
|
import { stdout, stderr } from "process";
|
||||||
import mockFs from "mock-fs";
|
import mockFs from "mock-fs";
|
||||||
@ -120,12 +119,9 @@ describe("kube auth proxy tests", () => {
|
|||||||
createCluster = di.inject(createClusterInjectionToken);
|
createCluster = di.inject(createClusterInjectionToken);
|
||||||
|
|
||||||
createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable);
|
createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable);
|
||||||
|
|
||||||
UserStore.createInstance();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
UserStore.resetInstance();
|
|
||||||
mockFs.restore();
|
mockFs.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -3,16 +3,17 @@
|
|||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||||
*/
|
*/
|
||||||
import { getInjectable } from "@ogre-tools/injectable";
|
import { getInjectable } from "@ogre-tools/injectable";
|
||||||
import packageInfo from "../../../../package.json";
|
|
||||||
import isDevelopmentInjectable from "../../../common/vars/is-development.injectable";
|
import isDevelopmentInjectable from "../../../common/vars/is-development.injectable";
|
||||||
|
import productNameInjectable from "./product-name.injectable";
|
||||||
|
|
||||||
const appNameInjectable = getInjectable({
|
const appNameInjectable = getInjectable({
|
||||||
id: "app-name",
|
id: "app-name",
|
||||||
|
|
||||||
instantiate: (di) => {
|
instantiate: (di) => {
|
||||||
const isDevelopment = di.inject(isDevelopmentInjectable);
|
const isDevelopment = di.inject(isDevelopmentInjectable);
|
||||||
|
const productName = di.inject(productNameInjectable);
|
||||||
|
|
||||||
return `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`;
|
return `${productName}${isDevelopment ? "Dev" : ""}`;
|
||||||
},
|
},
|
||||||
|
|
||||||
causesSideEffects: true,
|
causesSideEffects: true,
|
||||||
|
|||||||
14
src/main/app-paths/app-name/product-name.injectable.ts
Normal file
14
src/main/app-paths/app-name/product-name.injectable.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 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 packageInfo from "../../../../package.json";
|
||||||
|
|
||||||
|
const productNameInjectable = getInjectable({
|
||||||
|
id: "product-name",
|
||||||
|
instantiate: () => packageInfo.productName,
|
||||||
|
causesSideEffects: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default productNameInjectable;
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 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 { RequestChannelListener } from "../../common/utils/channel/request-channel-listener-injection-token";
|
||||||
|
import { requestChannelListenerInjectionToken } from "../../common/utils/channel/request-channel-listener-injection-token";
|
||||||
|
import type { AppPathsChannel } from "../../common/app-paths/app-paths-channel.injectable";
|
||||||
|
import appPathsChannelInjectable from "../../common/app-paths/app-paths-channel.injectable";
|
||||||
|
import appPathsInjectable from "../../common/app-paths/app-paths.injectable";
|
||||||
|
|
||||||
|
const appPathsRequestChannelListenerInjectable = getInjectable({
|
||||||
|
id: "app-paths-request-channel-listener",
|
||||||
|
|
||||||
|
instantiate: (di): RequestChannelListener<AppPathsChannel> => {
|
||||||
|
const channel = di.inject(appPathsChannelInjectable);
|
||||||
|
const appPaths = di.inject(appPathsInjectable);
|
||||||
|
|
||||||
|
return {
|
||||||
|
channel,
|
||||||
|
handler: () => appPaths,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
injectionToken: requestChannelListenerInjectionToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default appPathsRequestChannelListenerInjectable;
|
||||||
@ -6,7 +6,6 @@ import electronAppInjectable from "../../electron-app/electron-app.injectable";
|
|||||||
import getElectronAppPathInjectable from "./get-electron-app-path.injectable";
|
import getElectronAppPathInjectable from "./get-electron-app-path.injectable";
|
||||||
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
|
||||||
import type { App } from "electron";
|
import type { App } from "electron";
|
||||||
import registerChannelInjectable from "../register-channel/register-channel.injectable";
|
|
||||||
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
|
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
|
||||||
import { joinPathsFake } from "../../../common/test-utils/join-paths-fake";
|
import { joinPathsFake } from "../../../common/test-utils/join-paths-fake";
|
||||||
|
|
||||||
@ -32,7 +31,6 @@ describe("get-electron-app-path", () => {
|
|||||||
} as App;
|
} as App;
|
||||||
|
|
||||||
di.override(electronAppInjectable, () => appStub);
|
di.override(electronAppInjectable, () => appStub);
|
||||||
di.override(registerChannelInjectable, () => () => undefined);
|
|
||||||
di.override(joinPathsInjectable, () => joinPathsFake);
|
di.override(joinPathsInjectable, () => joinPathsFake);
|
||||||
|
|
||||||
getElectronAppPath = di.inject(getElectronAppPathInjectable) as (name: string) => string;
|
getElectronAppPath = di.inject(getElectronAppPathInjectable) as (name: string) => string;
|
||||||
|
|||||||
@ -1,17 +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 ipcMainInjectable from "./ipc-main/ipc-main.injectable";
|
|
||||||
import { registerChannel } from "./register-channel";
|
|
||||||
|
|
||||||
const registerChannelInjectable = getInjectable({
|
|
||||||
id: "register-channel",
|
|
||||||
|
|
||||||
instantiate: (di) => registerChannel({
|
|
||||||
ipcMain: di.inject(ipcMainInjectable),
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
export default registerChannelInjectable;
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) OpenLens Authors. All rights reserved.
|
|
||||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
|
||||||
*/
|
|
||||||
import type { IpcMain } from "electron";
|
|
||||||
import type { Channel } from "../../../common/ipc-channel/channel";
|
|
||||||
|
|
||||||
interface Dependencies {
|
|
||||||
ipcMain: IpcMain;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const registerChannel =
|
|
||||||
({ ipcMain }: Dependencies) =>
|
|
||||||
<TChannel extends Channel<TInstance>, TInstance>(
|
|
||||||
channel: TChannel,
|
|
||||||
getValue: () => TInstance,
|
|
||||||
) =>
|
|
||||||
ipcMain.handle(channel.name, getValue);
|
|
||||||
@ -12,8 +12,6 @@ import appPathsStateInjectable from "../../common/app-paths/app-paths-state.inje
|
|||||||
import { pathNames } from "../../common/app-paths/app-path-names";
|
import { pathNames } from "../../common/app-paths/app-path-names";
|
||||||
import { fromPairs, map } from "lodash/fp";
|
import { fromPairs, map } from "lodash/fp";
|
||||||
import { pipeline } from "@ogre-tools/fp";
|
import { pipeline } from "@ogre-tools/fp";
|
||||||
import { appPathsIpcChannel } from "../../common/app-paths/app-path-injection-token";
|
|
||||||
import registerChannelInjectable from "./register-channel/register-channel.injectable";
|
|
||||||
import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
import joinPathsInjectable from "../../common/path/join-paths.injectable";
|
||||||
import { beforeElectronIsReadyInjectionToken } from "../start-main-application/runnable-tokens/before-electron-is-ready-injection-token";
|
import { beforeElectronIsReadyInjectionToken } from "../start-main-application/runnable-tokens/before-electron-is-ready-injection-token";
|
||||||
|
|
||||||
@ -25,7 +23,6 @@ const setupAppPathsInjectable = getInjectable({
|
|||||||
const appName = di.inject(appNameInjectable);
|
const appName = di.inject(appNameInjectable);
|
||||||
const getAppPath = di.inject(getElectronAppPathInjectable);
|
const getAppPath = di.inject(getElectronAppPathInjectable);
|
||||||
const appPathsState = di.inject(appPathsStateInjectable);
|
const appPathsState = di.inject(appPathsStateInjectable);
|
||||||
const registerChannel = di.inject(registerChannelInjectable);
|
|
||||||
const directoryForIntegrationTesting = di.inject(directoryForIntegrationTestingInjectable);
|
const directoryForIntegrationTesting = di.inject(directoryForIntegrationTestingInjectable);
|
||||||
const joinPaths = di.inject(joinPathsInjectable);
|
const joinPaths = di.inject(joinPathsInjectable);
|
||||||
|
|
||||||
@ -46,8 +43,6 @@ const setupAppPathsInjectable = getInjectable({
|
|||||||
) as AppPaths;
|
) as AppPaths;
|
||||||
|
|
||||||
appPathsState.set(appPaths);
|
appPathsState.set(appPaths);
|
||||||
|
|
||||||
registerChannel(appPathsIpcChannel, () => appPaths);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user