@omega.js/desktop 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +98 -0
- package/README.md +166 -0
- package/bin/omega +2 -0
- package/bin/omega-desktop +2 -0
- package/bin/omg +2 -0
- package/dist/assets/css/core/_fontawesome.scss +146 -0
- package/dist/assets/css/core/_initialize.scss +38 -0
- package/dist/assets/css/omega-desktop.scss +39 -0
- package/dist/assets/css/shell/_index.scss +207 -0
- package/dist/assets/css/tokens/_index.scss +166 -0
- package/dist/assets/js/bootstrap.bundle.js +1 -0
- package/dist/assets/js/core/app-shell.js +136 -0
- package/dist/assets/themes/_template/README.md +115 -0
- package/dist/assets/themes/_template/_config.scss +60 -0
- package/dist/assets/themes/_template/_theme.js +14 -0
- package/dist/assets/themes/_template/_theme.scss +28 -0
- package/dist/assets/themes/_template/css/base/_root.scss +19 -0
- package/dist/assets/themes/_template/css/components/_components.scss +23 -0
- package/dist/assets/themes/base/README.md +5 -0
- package/dist/assets/themes/base/_components/data/org-chart/component.html +33 -0
- package/dist/assets/themes/base/_components/data/org-chart/component.json5 +33 -0
- package/dist/assets/themes/base/_components/data/org-chart/component.scss +183 -0
- package/dist/assets/themes/base/_components/heading/masthead/component.html +22 -0
- package/dist/assets/themes/base/_components/heading/masthead/component.json5 +30 -0
- package/dist/assets/themes/base/_components/heading/section-head/component.html +18 -0
- package/dist/assets/themes/base/_components/heading/section-head/component.json5 +27 -0
- package/dist/assets/themes/base/_components/pricing/features/component.html +29 -0
- package/dist/assets/themes/base/_components/pricing/features/component.json5 +30 -0
- package/dist/assets/themes/base/_hero/orbit/index.html +30 -0
- package/dist/assets/themes/base/_hero/orbit/script.js +33 -0
- package/dist/assets/themes/base/_hero/orbit/style.scss +133 -0
- package/dist/assets/themes/base/_includes/admin/sections/sidebar.html +3 -0
- package/dist/assets/themes/base/_includes/admin/sections/topbar.html +3 -0
- package/dist/assets/themes/base/_includes/backend/sections/sidebar.html +3 -0
- package/dist/assets/themes/base/_includes/backend/sections/topbar.html +3 -0
- package/dist/assets/themes/base/_includes/frontend/components/connection-card.html +49 -0
- package/dist/assets/themes/base/_includes/frontend/components/post-card.html +54 -0
- package/dist/assets/themes/base/_includes/frontend/sections/account-connections.html +42 -0
- package/dist/assets/themes/base/_includes/frontend/sections/account-section-header.html +11 -0
- package/dist/assets/themes/base/_includes/frontend/sections/footer.html +154 -0
- package/dist/assets/themes/base/_includes/frontend/sections/nav.html +276 -0
- package/dist/assets/themes/base/_includes/global/sections/account.html +72 -0
- package/dist/assets/themes/base/_includes/global/sections/app-sidebar.html +248 -0
- package/dist/assets/themes/base/_includes/global/sections/app-topbar.html +228 -0
- package/dist/assets/themes/base/_includes/global/sections/page-header.html +99 -0
- package/dist/assets/themes/base/_layouts/admin/core/minimal-viewport-locked.html +22 -0
- package/dist/assets/themes/base/_layouts/admin/core/minimal.html +23 -0
- package/dist/assets/themes/base/_layouts/backend/core/base.html +57 -0
- package/dist/assets/themes/base/_layouts/backend/core/minimal-viewport-locked.html +32 -0
- package/dist/assets/themes/base/_layouts/backend/core/minimal.html +32 -0
- package/dist/assets/themes/base/_layouts/backend/pages/dashboard/index.html +230 -0
- package/dist/assets/themes/base/_layouts/frontend/core/base.html +31 -0
- package/dist/assets/themes/base/_layouts/frontend/core/cover.html +40 -0
- package/dist/assets/themes/base/_layouts/frontend/core/minimal.html +47 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/404.html +54 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/about.html +144 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/account/index.html +2104 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/alternative.html +319 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/alternatives/index.html +186 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/app.html +142 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/auth/reset.html +53 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/auth/signin.html +90 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/auth/signup.html +141 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/auth/token.html +44 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/blog/categories/category.html +88 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/blog/categories/index.html +88 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/blog/index.html +255 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/blog/post.html +261 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/blog/tags/index.html +84 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/blog/tags/tag.html +88 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/collection/category.html +72 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/collection/document.html +54 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/collection/index.html +84 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/connections/callback.html +55 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/contact.html +214 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/download.html +793 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/extension/index.html +253 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/extension/installed.html +198 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/feedback.html +171 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/index.html +66 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/legal/document.html +78 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/payment/checkout.html +459 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/payment/confirmation.html +131 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/portal/email-preferences.html +104 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/pricing.html +596 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/status.html +361 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/team/index.html +210 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/team/member.html +125 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/updates/index.html +119 -0
- package/dist/assets/themes/base/_layouts/frontend/pages/updates/update.html +118 -0
- package/dist/assets/themes/base/_sections/about/hero/section.html +68 -0
- package/dist/assets/themes/base/_sections/about/hero/section.json5 +58 -0
- package/dist/assets/themes/base/_sections/about/letter/section.html +60 -0
- package/dist/assets/themes/base/_sections/about/letter/section.json5 +56 -0
- package/dist/assets/themes/base/_sections/about/photo-band/section.html +24 -0
- package/dist/assets/themes/base/_sections/about/photo-band/section.json5 +23 -0
- package/dist/assets/themes/base/_sections/about/principles/section.html +17 -0
- package/dist/assets/themes/base/_sections/about/principles/section.json5 +34 -0
- package/dist/assets/themes/base/_sections/about/timeline/section.html +20 -0
- package/dist/assets/themes/base/_sections/about/timeline/section.json5 +34 -0
- package/dist/assets/themes/base/_sections/marketing/bento/section.html +91 -0
- package/dist/assets/themes/base/_sections/marketing/bento/section.json5 +152 -0
- package/dist/assets/themes/base/_sections/marketing/cta/section.html +44 -0
- package/dist/assets/themes/base/_sections/marketing/cta/section.json5 +47 -0
- package/dist/assets/themes/base/_sections/marketing/faq/section.html +52 -0
- package/dist/assets/themes/base/_sections/marketing/faq/section.json5 +68 -0
- package/dist/assets/themes/base/_sections/marketing/hero/section.html +349 -0
- package/dist/assets/themes/base/_sections/marketing/hero/section.json5 +335 -0
- package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.html +38 -0
- package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.js +86 -0
- package/dist/assets/themes/base/_sections/marketing/newsletter-cta/section.json5 +29 -0
- package/dist/assets/themes/base/_sections/marketing/pricing-cards/section.html +115 -0
- package/dist/assets/themes/base/_sections/marketing/pricing-cards/section.json5 +72 -0
- package/dist/assets/themes/base/_sections/marketing/product-demo/section.html +62 -0
- package/dist/assets/themes/base/_sections/marketing/product-demo/section.js +44 -0
- package/dist/assets/themes/base/_sections/marketing/product-demo/section.json5 +52 -0
- package/dist/assets/themes/base/_sections/marketing/prose/section.html +22 -0
- package/dist/assets/themes/base/_sections/marketing/prose/section.json5 +41 -0
- package/dist/assets/themes/base/_sections/marketing/showcase/section.html +35 -0
- package/dist/assets/themes/base/_sections/marketing/showcase/section.json5 +52 -0
- package/dist/assets/themes/base/_sections/marketing/stats/section.html +32 -0
- package/dist/assets/themes/base/_sections/marketing/stats/section.json5 +44 -0
- package/dist/assets/themes/base/_sections/marketing/testimonials/section.html +54 -0
- package/dist/assets/themes/base/_sections/marketing/testimonials/section.json5 +70 -0
- package/dist/assets/themes/base/_sections/marketing/trusted-by/section.html +30 -0
- package/dist/assets/themes/base/_sections/marketing/trusted-by/section.json5 +38 -0
- package/dist/assets/themes/base/_sections/verts/unit/section.html +7 -0
- package/dist/assets/themes/base/_sections/verts/unit/section.js +22 -0
- package/dist/assets/themes/base/_sections/verts/unit/section.json5 +24 -0
- package/dist/assets/themes/base/_sections/verts/unit/section.scss +24 -0
- package/dist/assets/themes/base/_theme.js +13 -0
- package/dist/assets/themes/base/_theme.scss +4 -0
- package/dist/assets/themes/bootstrap/_theme.js +16 -0
- package/dist/assets/themes/bootstrap/_theme.scss +11 -0
- package/dist/assets/themes/bootstrap/js/index.esm.js +19 -0
- package/dist/assets/themes/bootstrap/js/index.umd.js +34 -0
- package/dist/assets/themes/bootstrap/js/src/alert.js +87 -0
- package/dist/assets/themes/bootstrap/js/src/base-component.js +86 -0
- package/dist/assets/themes/bootstrap/js/src/button.js +72 -0
- package/dist/assets/themes/bootstrap/js/src/carousel.js +474 -0
- package/dist/assets/themes/bootstrap/js/src/collapse.js +297 -0
- package/dist/assets/themes/bootstrap/js/src/dom/data.js +55 -0
- package/dist/assets/themes/bootstrap/js/src/dom/event-handler.js +317 -0
- package/dist/assets/themes/bootstrap/js/src/dom/manipulator.js +71 -0
- package/dist/assets/themes/bootstrap/js/src/dom/selector-engine.js +126 -0
- package/dist/assets/themes/bootstrap/js/src/dropdown.js +458 -0
- package/dist/assets/themes/bootstrap/js/src/modal.js +378 -0
- package/dist/assets/themes/bootstrap/js/src/offcanvas.js +282 -0
- package/dist/assets/themes/bootstrap/js/src/popover.js +97 -0
- package/dist/assets/themes/bootstrap/js/src/scrollspy.js +296 -0
- package/dist/assets/themes/bootstrap/js/src/tab.js +315 -0
- package/dist/assets/themes/bootstrap/js/src/toast.js +224 -0
- package/dist/assets/themes/bootstrap/js/src/tooltip.js +632 -0
- package/dist/assets/themes/bootstrap/js/src/util/backdrop.js +151 -0
- package/dist/assets/themes/bootstrap/js/src/util/component-functions.js +35 -0
- package/dist/assets/themes/bootstrap/js/src/util/config.js +65 -0
- package/dist/assets/themes/bootstrap/js/src/util/focustrap.js +115 -0
- package/dist/assets/themes/bootstrap/js/src/util/index.js +306 -0
- package/dist/assets/themes/bootstrap/js/src/util/sanitizer.js +117 -0
- package/dist/assets/themes/bootstrap/js/src/util/scrollbar.js +114 -0
- package/dist/assets/themes/bootstrap/js/src/util/swipe.js +146 -0
- package/dist/assets/themes/bootstrap/js/src/util/template-factory.js +160 -0
- package/dist/assets/themes/bootstrap/overrides/_accordion.scss +21 -0
- package/dist/assets/themes/bootstrap/overrides/_avatars.scss +51 -0
- package/dist/assets/themes/bootstrap/overrides/_blog-images.scss +45 -0
- package/dist/assets/themes/bootstrap/overrides/_buttons-adaptive.scss +141 -0
- package/dist/assets/themes/bootstrap/overrides/_color-shades.scss +25 -0
- package/dist/assets/themes/bootstrap/overrides/_cursor-utilities.scss +13 -0
- package/dist/assets/themes/bootstrap/overrides/_index.scss +30 -0
- package/dist/assets/themes/bootstrap/overrides/_links.scss +26 -0
- package/dist/assets/themes/bootstrap/overrides/_soft-colors.scss +99 -0
- package/dist/assets/themes/bootstrap/overrides/_spacing-extended.scss +74 -0
- package/dist/assets/themes/bootstrap/overrides/_spinners.scss +22 -0
- package/dist/assets/themes/bootstrap/overrides/_typography-utilities.scss +40 -0
- package/dist/assets/themes/bootstrap/scss/_accordion.scss +153 -0
- package/dist/assets/themes/bootstrap/scss/_alert.scss +68 -0
- package/dist/assets/themes/bootstrap/scss/_badge.scss +38 -0
- package/dist/assets/themes/bootstrap/scss/_breadcrumb.scss +40 -0
- package/dist/assets/themes/bootstrap/scss/_button-group.scss +147 -0
- package/dist/assets/themes/bootstrap/scss/_buttons.scss +216 -0
- package/dist/assets/themes/bootstrap/scss/_card.scss +238 -0
- package/dist/assets/themes/bootstrap/scss/_carousel.scss +226 -0
- package/dist/assets/themes/bootstrap/scss/_close.scss +66 -0
- package/dist/assets/themes/bootstrap/scss/_containers.scss +41 -0
- package/dist/assets/themes/bootstrap/scss/_dropdown.scss +250 -0
- package/dist/assets/themes/bootstrap/scss/_forms.scss +9 -0
- package/dist/assets/themes/bootstrap/scss/_functions.scss +302 -0
- package/dist/assets/themes/bootstrap/scss/_grid.scss +39 -0
- package/dist/assets/themes/bootstrap/scss/_helpers.scss +12 -0
- package/dist/assets/themes/bootstrap/scss/_images.scss +42 -0
- package/dist/assets/themes/bootstrap/scss/_list-group.scss +199 -0
- package/dist/assets/themes/bootstrap/scss/_maps.scss +174 -0
- package/dist/assets/themes/bootstrap/scss/_mixins.scss +42 -0
- package/dist/assets/themes/bootstrap/scss/_modal.scss +240 -0
- package/dist/assets/themes/bootstrap/scss/_nav.scss +197 -0
- package/dist/assets/themes/bootstrap/scss/_navbar.scss +289 -0
- package/dist/assets/themes/bootstrap/scss/_offcanvas.scss +147 -0
- package/dist/assets/themes/bootstrap/scss/_pagination.scss +109 -0
- package/dist/assets/themes/bootstrap/scss/_placeholders.scss +51 -0
- package/dist/assets/themes/bootstrap/scss/_popover.scss +196 -0
- package/dist/assets/themes/bootstrap/scss/_progress.scss +68 -0
- package/dist/assets/themes/bootstrap/scss/_reboot.scss +611 -0
- package/dist/assets/themes/bootstrap/scss/_root.scss +187 -0
- package/dist/assets/themes/bootstrap/scss/_spinners.scss +85 -0
- package/dist/assets/themes/bootstrap/scss/_tables.scss +171 -0
- package/dist/assets/themes/bootstrap/scss/_toasts.scss +73 -0
- package/dist/assets/themes/bootstrap/scss/_tooltip.scss +119 -0
- package/dist/assets/themes/bootstrap/scss/_transitions.scss +27 -0
- package/dist/assets/themes/bootstrap/scss/_type.scss +106 -0
- package/dist/assets/themes/bootstrap/scss/_utilities.scss +806 -0
- package/dist/assets/themes/bootstrap/scss/_variables-dark.scss +102 -0
- package/dist/assets/themes/bootstrap/scss/_variables.scss +1753 -0
- package/dist/assets/themes/bootstrap/scss/bootstrap-grid.scss +62 -0
- package/dist/assets/themes/bootstrap/scss/bootstrap-reboot.scss +10 -0
- package/dist/assets/themes/bootstrap/scss/bootstrap-utilities.scss +19 -0
- package/dist/assets/themes/bootstrap/scss/bootstrap.scss +52 -0
- package/dist/assets/themes/bootstrap/scss/forms/_floating-labels.scss +97 -0
- package/dist/assets/themes/bootstrap/scss/forms/_form-check.scss +189 -0
- package/dist/assets/themes/bootstrap/scss/forms/_form-control.scss +214 -0
- package/dist/assets/themes/bootstrap/scss/forms/_form-range.scss +91 -0
- package/dist/assets/themes/bootstrap/scss/forms/_form-select.scss +80 -0
- package/dist/assets/themes/bootstrap/scss/forms/_form-text.scss +11 -0
- package/dist/assets/themes/bootstrap/scss/forms/_input-group.scss +132 -0
- package/dist/assets/themes/bootstrap/scss/forms/_labels.scss +36 -0
- package/dist/assets/themes/bootstrap/scss/forms/_validation.scss +12 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_clearfix.scss +3 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_color-bg.scss +7 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_colored-links.scss +30 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_focus-ring.scss +5 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_icon-link.scss +25 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_position.scss +36 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_ratio.scss +26 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_stacks.scss +15 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_stretched-link.scss +15 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_text-truncation.scss +7 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_visually-hidden.scss +8 -0
- package/dist/assets/themes/bootstrap/scss/helpers/_vr.scss +8 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_alert.scss +18 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_backdrop.scss +14 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_banner.scss +7 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_border-radius.scss +78 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_box-shadow.scss +18 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_breakpoints.scss +127 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_buttons.scss +70 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_caret.scss +69 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_clearfix.scss +9 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_color-mode.scss +21 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_color-scheme.scss +7 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_container.scss +11 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_deprecate.scss +10 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_forms.scss +163 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_gradients.scss +47 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_grid.scss +151 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_image.scss +16 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_list-group.scss +26 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_lists.scss +7 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_pagination.scss +10 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_reset-text.scss +17 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_resize.scss +6 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_table-variants.scss +24 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_text-truncate.scss +8 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_transition.scss +26 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_utilities.scss +97 -0
- package/dist/assets/themes/bootstrap/scss/mixins/_visually-hidden.scss +38 -0
- package/dist/assets/themes/bootstrap/scss/tests/jasmine.js +16 -0
- package/dist/assets/themes/bootstrap/scss/tests/mixins/_auto-import-of-variables-dark.test.scss +7 -0
- package/dist/assets/themes/bootstrap/scss/tests/mixins/_color-modes.test.scss +69 -0
- package/dist/assets/themes/bootstrap/scss/tests/mixins/_media-query-color-mode-full.test.scss +8 -0
- package/dist/assets/themes/bootstrap/scss/tests/mixins/_utilities.test.scss +393 -0
- package/dist/assets/themes/bootstrap/scss/tests/sass-true/register.js +14 -0
- package/dist/assets/themes/bootstrap/scss/tests/sass-true/runner.js +17 -0
- package/dist/assets/themes/bootstrap/scss/tests/utilities/_api.test.scss +75 -0
- package/dist/assets/themes/bootstrap/scss/utilities/_api.scss +47 -0
- package/dist/assets/themes/bootstrap/scss/vendor/_rfs.scss +348 -0
- package/dist/assets/themes/classy/README.md +89 -0
- package/dist/assets/themes/classy/_config.scss +245 -0
- package/dist/assets/themes/classy/_theme.js +31 -0
- package/dist/assets/themes/classy/_theme.scss +57 -0
- package/dist/assets/themes/classy/css/app/_panels.scss +183 -0
- package/dist/assets/themes/classy/css/base/_fonts.scss +79 -0
- package/dist/assets/themes/classy/css/base/_no-raise.scss +49 -0
- package/dist/assets/themes/classy/css/base/_root.scss +113 -0
- package/dist/assets/themes/classy/css/base/_typography.scss +154 -0
- package/dist/assets/themes/classy/css/base/_utilities.scss +284 -0
- package/dist/assets/themes/classy/css/components/_alerts.scss +51 -0
- package/dist/assets/themes/classy/css/components/_badges.scss +46 -0
- package/dist/assets/themes/classy/css/components/_buttons.scss +175 -0
- package/dist/assets/themes/classy/css/components/_cards.scss +41 -0
- package/dist/assets/themes/classy/css/components/_dropdowns.scss +48 -0
- package/dist/assets/themes/classy/css/components/_forms.scss +171 -0
- package/dist/assets/themes/classy/css/components/_receipt.scss +45 -0
- package/dist/assets/themes/classy/css/layout/_footer.scss +298 -0
- package/dist/assets/themes/classy/css/layout/_general.scss +209 -0
- package/dist/assets/themes/classy/css/layout/_nav.scss +154 -0
- package/dist/assets/themes/classy/css/layout/_shell.scss +458 -0
- package/dist/assets/themes/classy/css/marketing/_bento.scss +238 -0
- package/dist/assets/themes/classy/css/marketing/_content.scss +1328 -0
- package/dist/assets/themes/classy/css/marketing/_downloads.scss +246 -0
- package/dist/assets/themes/classy/css/marketing/_hero.scss +311 -0
- package/dist/assets/themes/classy/css/marketing/_sections.scss +649 -0
- package/dist/assets/themes/classy/css/pages/404/index.scss +67 -0
- package/dist/assets/themes/classy/css/pages/_auth.scss +235 -0
- package/dist/assets/themes/classy/css/pages/admin/firebase/index.scss +22 -0
- package/dist/assets/themes/classy/css/pages/blog/[slug].scss +56 -0
- package/dist/assets/themes/classy/css/pages/cookies.scss +2 -0
- package/dist/assets/themes/classy/css/pages/dashboard/account/index.scss +31 -0
- package/dist/assets/themes/classy/css/pages/feedback/index.scss +39 -0
- package/dist/assets/themes/classy/css/pages/legal/_document.scss +192 -0
- package/dist/assets/themes/classy/css/pages/payment/checkout/index.scss +585 -0
- package/dist/assets/themes/classy/css/pages/payment/confirmation/index.scss +9 -0
- package/dist/assets/themes/classy/css/pages/privacy.scss +2 -0
- package/dist/assets/themes/classy/css/pages/status/index.scss +164 -0
- package/dist/assets/themes/classy/css/pages/terms.scss +2 -0
- package/dist/assets/themes/classy/css/pages/updates/index.scss +128 -0
- package/dist/assets/themes/classy/fonts/inter-italic-latin-ext.woff2 +0 -0
- package/dist/assets/themes/classy/fonts/inter-italic-latin.woff2 +0 -0
- package/dist/assets/themes/classy/fonts/inter-normal-latin-ext.woff2 +0 -0
- package/dist/assets/themes/classy/fonts/inter-normal-latin.woff2 +0 -0
- package/dist/assets/themes/classy/fonts/newsreader-italic-latin-ext.woff2 +0 -0
- package/dist/assets/themes/classy/fonts/newsreader-italic-latin.woff2 +0 -0
- package/dist/assets/themes/classy/fonts/newsreader-normal-latin-ext.woff2 +0 -0
- package/dist/assets/themes/classy/fonts/newsreader-normal-latin.woff2 +0 -0
- package/dist/assets/themes/classy/js/hero-demo-form.js +56 -0
- package/dist/assets/themes/neobrutalism/README.md +118 -0
- package/dist/assets/themes/neobrutalism/_config.scss +139 -0
- package/dist/assets/themes/neobrutalism/_layouts/frontend/core/base.html +30 -0
- package/dist/assets/themes/neobrutalism/_layouts/frontend/pages/index.html +275 -0
- package/dist/assets/themes/neobrutalism/_theme.js +24 -0
- package/dist/assets/themes/neobrutalism/_theme.scss +61 -0
- package/dist/assets/themes/neobrutalism/css/base/_mixins.scss +46 -0
- package/dist/assets/themes/neobrutalism/css/base/_root.scss +169 -0
- package/dist/assets/themes/neobrutalism/css/base/_typography.scss +61 -0
- package/dist/assets/themes/neobrutalism/css/base/_utilities.scss +89 -0
- package/dist/assets/themes/neobrutalism/css/components/_billing-toggle.scss +74 -0
- package/dist/assets/themes/neobrutalism/css/components/_buttons.scss +148 -0
- package/dist/assets/themes/neobrutalism/css/components/_cards.scss +47 -0
- package/dist/assets/themes/neobrutalism/css/components/_forms.scss +88 -0
- package/dist/assets/themes/neobrutalism/css/components/_marketing.scss +244 -0
- package/dist/assets/themes/neobrutalism/css/layout/_general.scss +213 -0
- package/dist/assets/themes/neobrutalism/css/layout/_navigation.scss +171 -0
- package/dist/assets/themes/neobrutalism/css/pages/index.scss +203 -0
- package/dist/assets/themes/neobrutalism/css/pages/pricing/index.scss +58 -0
- package/dist/assets/themes/neobrutalism/css/pages/test/libraries/layers/index.scss +7 -0
- package/dist/assets/themes/neobrutalism/js/pages/test/libraries/layers/index.js +9 -0
- package/dist/assets/themes/newsflash/README.md +117 -0
- package/dist/assets/themes/newsflash/_components/heading/lede/component.html +14 -0
- package/dist/assets/themes/newsflash/_components/heading/lede/component.json5 +23 -0
- package/dist/assets/themes/newsflash/_components/heading/rule-head/component.html +17 -0
- package/dist/assets/themes/newsflash/_components/heading/rule-head/component.json5 +23 -0
- package/dist/assets/themes/newsflash/_components/news/byline/component.html +12 -0
- package/dist/assets/themes/newsflash/_components/news/byline/component.json5 +18 -0
- package/dist/assets/themes/newsflash/_components/news/story-card/component.html +25 -0
- package/dist/assets/themes/newsflash/_components/news/story-card/component.json5 +21 -0
- package/dist/assets/themes/newsflash/_config.scss +147 -0
- package/dist/assets/themes/newsflash/_layouts/frontend/core/base.html +51 -0
- package/dist/assets/themes/newsflash/_layouts/frontend/pages/blog/index.html +299 -0
- package/dist/assets/themes/newsflash/_layouts/frontend/pages/blog/post.html +235 -0
- package/dist/assets/themes/newsflash/_layouts/frontend/pages/index.html +328 -0
- package/dist/assets/themes/newsflash/_sections/marketing/desks/section.html +39 -0
- package/dist/assets/themes/newsflash/_sections/marketing/desks/section.json5 +49 -0
- package/dist/assets/themes/newsflash/_sections/marketing/newsletter-cta/section.html +103 -0
- package/dist/assets/themes/newsflash/_sections/marketing/newsletter-cta/section.json5 +50 -0
- package/dist/assets/themes/newsflash/_sections/marketing/rundown/section.html +28 -0
- package/dist/assets/themes/newsflash/_sections/marketing/rundown/section.json5 +40 -0
- package/dist/assets/themes/newsflash/_theme.js +24 -0
- package/dist/assets/themes/newsflash/_theme.scss +70 -0
- package/dist/assets/themes/newsflash/css/base/_fonts.scss +80 -0
- package/dist/assets/themes/newsflash/css/base/_mixins.scss +51 -0
- package/dist/assets/themes/newsflash/css/base/_root.scss +257 -0
- package/dist/assets/themes/newsflash/css/base/_typography.scss +49 -0
- package/dist/assets/themes/newsflash/css/base/_utilities.scss +122 -0
- package/dist/assets/themes/newsflash/css/components/_badges.scss +65 -0
- package/dist/assets/themes/newsflash/css/components/_billing-toggle.scss +61 -0
- package/dist/assets/themes/newsflash/css/components/_buttons.scss +139 -0
- package/dist/assets/themes/newsflash/css/components/_cards.scss +47 -0
- package/dist/assets/themes/newsflash/css/components/_editorial.scss +173 -0
- package/dist/assets/themes/newsflash/css/components/_forms.scss +75 -0
- package/dist/assets/themes/newsflash/css/components/_infinite-scroll.scss +102 -0
- package/dist/assets/themes/newsflash/css/components/_panels.scss +156 -0
- package/dist/assets/themes/newsflash/css/components/_ticker.scss +67 -0
- package/dist/assets/themes/newsflash/css/layout/_general.scss +330 -0
- package/dist/assets/themes/newsflash/css/layout/_navigation.scss +168 -0
- package/dist/assets/themes/newsflash/css/pages/404/index.scss +79 -0
- package/dist/assets/themes/newsflash/css/pages/blog/[slug].scss +186 -0
- package/dist/assets/themes/newsflash/css/pages/blog/index.scss +17 -0
- package/dist/assets/themes/newsflash/css/pages/index.scss +157 -0
- package/dist/assets/themes/newsflash/css/pages/pricing/index.scss +55 -0
- package/dist/assets/themes/newsflash/css/pages/test/libraries/layers/index.scss +7 -0
- package/dist/assets/themes/newsflash/fonts/fraunces-italic-latin-ext.woff2 +0 -0
- package/dist/assets/themes/newsflash/fonts/fraunces-italic-latin.woff2 +0 -0
- package/dist/assets/themes/newsflash/fonts/fraunces-normal-latin-ext.woff2 +0 -0
- package/dist/assets/themes/newsflash/fonts/fraunces-normal-latin.woff2 +0 -0
- package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-italic-latin-ext.woff2 +0 -0
- package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-italic-latin.woff2 +0 -0
- package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-normal-latin-ext.woff2 +0 -0
- package/dist/assets/themes/newsflash/fonts/schibsted-grotesk-normal-latin.woff2 +0 -0
- package/dist/assets/themes/newsflash/js/pages/blog/[slug].js +29 -0
- package/dist/assets/themes/newsflash/js/pages/test/libraries/layers/index.js +9 -0
- package/dist/build.js +249 -0
- package/dist/cli-run.js +34 -0
- package/dist/cli.js +49 -0
- package/dist/commands/build.js +32 -0
- package/dist/commands/cdp/capture.js +91 -0
- package/dist/commands/cdp/client.js +140 -0
- package/dist/commands/cdp/eval.js +20 -0
- package/dist/commands/cdp/quit.js +84 -0
- package/dist/commands/cdp/relaunch.js +62 -0
- package/dist/commands/cdp/shot.js +20 -0
- package/dist/commands/cdp/status.js +38 -0
- package/dist/commands/cdp/theme.js +21 -0
- package/dist/commands/cdp.js +53 -0
- package/dist/commands/clean.js +34 -0
- package/dist/commands/deploy.js +75 -0
- package/dist/commands/finalize-release.js +238 -0
- package/dist/commands/install.js +37 -0
- package/dist/commands/launch.js +150 -0
- package/dist/commands/lib/dependencies.js +35 -0
- package/dist/commands/lib/deploy-precheck.js +117 -0
- package/dist/commands/lib/ensure-target.js +300 -0
- package/dist/commands/logs.js +185 -0
- package/dist/commands/package.js +31 -0
- package/dist/commands/publish.js +34 -0
- package/dist/commands/push-secrets.js +141 -0
- package/dist/commands/release.js +286 -0
- package/dist/commands/runner.js +1874 -0
- package/dist/commands/sign-windows.js +563 -0
- package/dist/commands/test.js +121 -0
- package/dist/commands/update.js +21 -0
- package/dist/commands/validate-certs.js +238 -0
- package/dist/commands/version.js +9 -0
- package/dist/config/page-template.html +35 -0
- package/dist/defaults/.github/workflows/build.yml +276 -0
- package/dist/defaults/AGENTS.md +109 -0
- package/dist/defaults/CHANGELOG.md +15 -0
- package/dist/defaults/CLAUDE.md +1 -0
- package/dist/defaults/_.gitignore +24 -0
- package/dist/defaults/_mas/README.md +49 -0
- package/dist/defaults/_mas/entitlements.mas.inherit.plist +12 -0
- package/dist/defaults/_mas/entitlements.mas.loginhelper.plist +8 -0
- package/dist/defaults/_mas/entitlements.mas.plist +33 -0
- package/dist/defaults/config/certs/.gitkeep +0 -0
- package/dist/defaults/config/certs/README.md +60 -0
- package/dist/defaults/config/icons/macos/dmg.png +0 -0
- package/dist/defaults/config/icons/macos/icon.png +0 -0
- package/dist/defaults/config/icons/macos/tray.png +0 -0
- package/dist/defaults/config/icons/windows/icon.png +0 -0
- package/dist/defaults/config/omega.json5 +279 -0
- package/dist/defaults/docs/README.md +17 -0
- package/dist/defaults/gulpfile.js +6 -0
- package/dist/defaults/hooks/build/post.js +9 -0
- package/dist/defaults/hooks/build/pre.js +9 -0
- package/dist/defaults/hooks/notarize/post.js +16 -0
- package/dist/defaults/hooks/release/post.js +9 -0
- package/dist/defaults/hooks/release/pre.js +9 -0
- package/dist/defaults/src/assets/js/components/about/index.js +10 -0
- package/dist/defaults/src/assets/js/components/main/index.js +14 -0
- package/dist/defaults/src/assets/js/components/settings/index.js +10 -0
- package/dist/defaults/src/assets/scss/main.scss +34 -0
- package/dist/defaults/src/assets/scss/pages/about.scss +3 -0
- package/dist/defaults/src/assets/scss/pages/main.scss +4 -0
- package/dist/defaults/src/assets/scss/pages/settings.scss +3 -0
- package/dist/defaults/src/integrations/context-menu/index.js +60 -0
- package/dist/defaults/src/integrations/menu/index.js +58 -0
- package/dist/defaults/src/integrations/tray/index.js +60 -0
- package/dist/defaults/src/main.js +78 -0
- package/dist/defaults/src/preload.js +15 -0
- package/dist/defaults/src/views/about/index.html +3 -0
- package/dist/defaults/src/views/main/index.html +8 -0
- package/dist/defaults/src/views/settings/index.html +3 -0
- package/dist/defaults/test/README.md +41 -0
- package/dist/defaults/test/_init.js +10 -0
- package/dist/gulp/main.js +144 -0
- package/dist/gulp/tasks/audit.js +82 -0
- package/dist/gulp/tasks/build-config.js +445 -0
- package/dist/gulp/tasks/bundle.js +451 -0
- package/dist/gulp/tasks/defaults.js +20 -0
- package/dist/gulp/tasks/distribute.js +8 -0
- package/dist/gulp/tasks/html.js +95 -0
- package/dist/gulp/tasks/package-quick.js +102 -0
- package/dist/gulp/tasks/package.js +53 -0
- package/dist/gulp/tasks/release.js +57 -0
- package/dist/gulp/tasks/sass.js +82 -0
- package/dist/gulp/tasks/serve.js +191 -0
- package/dist/hooks/notarize.js +70 -0
- package/dist/index.js +14 -0
- package/dist/lib/_menu-mixin.js +199 -0
- package/dist/lib/analytics.js +360 -0
- package/dist/lib/app-state.js +220 -0
- package/dist/lib/auth-flow.js +160 -0
- package/dist/lib/auth-persistence.js +207 -0
- package/dist/lib/auto-updater.js +752 -0
- package/dist/lib/client-bridge.js +371 -0
- package/dist/lib/context-menu.js +302 -0
- package/dist/lib/context.js +162 -0
- package/dist/lib/deep-link.js +370 -0
- package/dist/lib/fontawesome.js +203 -0
- package/dist/lib/ipc.js +169 -0
- package/dist/lib/logger-lite.js +259 -0
- package/dist/lib/logger.js +4 -0
- package/dist/lib/menu.js +521 -0
- package/dist/lib/protocol.js +85 -0
- package/dist/lib/remote-config.js +224 -0
- package/dist/lib/remote-scripts.js +158 -0
- package/dist/lib/restart-manager/index.js +550 -0
- package/dist/lib/restart-manager/install.js +353 -0
- package/dist/lib/restart-manager/protocol.js +274 -0
- package/dist/lib/sign-helpers/auto-unlock.js +95 -0
- package/dist/lib/sign-helpers/entitlements.js +98 -0
- package/dist/lib/sign-helpers/resolve-icons.js +171 -0
- package/dist/lib/sign-helpers/sign-events.js +75 -0
- package/dist/lib/sign-helpers/update-info.js +186 -0
- package/dist/lib/startup.js +214 -0
- package/dist/lib/storage.js +193 -0
- package/dist/lib/templating.js +85 -0
- package/dist/lib/theme.js +173 -0
- package/dist/lib/tray.js +414 -0
- package/dist/lib/usage.js +141 -0
- package/dist/lib/window-manager.js +509 -0
- package/dist/main.js +542 -0
- package/dist/omega-bin.js +6 -0
- package/dist/preload.js +241 -0
- package/dist/renderer.js +456 -0
- package/dist/test/assert.js +4 -0
- package/dist/test/fixtures/consumer-app/config/omega.json5 +39 -0
- package/dist/test/fixtures/consumer-app/package.json +13 -0
- package/dist/test/fixtures/consumer-app/src/assets/js/components/main/index.js +11 -0
- package/dist/test/fixtures/consumer-app/src/assets/scss/main.scss +3 -0
- package/dist/test/fixtures/consumer-app/src/main.js +17 -0
- package/dist/test/fixtures/consumer-app/src/preload.js +4 -0
- package/dist/test/fixtures/consumer-app/src/views/main/index.html +4 -0
- package/dist/test/harness/boot-entry.js +237 -0
- package/dist/test/harness/main-entry.js +494 -0
- package/dist/test/harness/package.json +6 -0
- package/dist/test/harness/renderer-entry.js +173 -0
- package/dist/test/harness/renderer-preload.js +310 -0
- package/dist/test/harness/renderer.html +12 -0
- package/dist/test/index.js +65 -0
- package/dist/test/runner.js +99 -0
- package/dist/test/runners/boot.js +414 -0
- package/dist/test/runners/electron.js +92 -0
- package/dist/test/runners/render-event.js +46 -0
- package/dist/test/suites/boot/build-isolation.test.js +54 -0
- package/dist/test/suites/boot/consumer-app-boots.test.js +99 -0
- package/dist/test/suites/boot/restart-manager.test.js +38 -0
- package/dist/test/suites/boot/storage-bundled.test.js +59 -0
- package/dist/test/suites/boot/theme.test.js +68 -0
- package/dist/test/suites/boot/view-suite.test.js +47 -0
- package/dist/test/suites/build/attach-log-file.test.js +99 -0
- package/dist/test/suites/build/audit.test.js +271 -0
- package/dist/test/suites/build/boot-fixture.test.js +48 -0
- package/dist/test/suites/build/build-config.test.js +457 -0
- package/dist/test/suites/build/build-verbs.test.js +156 -0
- package/dist/test/suites/build/build-workflow-jobs.test.js +55 -0
- package/dist/test/suites/build/cdp.test.js +89 -0
- package/dist/test/suites/build/cli.test.js +135 -0
- package/dist/test/suites/build/config-schema.test.js +133 -0
- package/dist/test/suites/build/defaults-scaffold.test.js +192 -0
- package/dist/test/suites/build/deliver-certs.test.js +95 -0
- package/dist/test/suites/build/derive-signing-env.test.js +122 -0
- package/dist/test/suites/build/electron-node-version.test.js +49 -0
- package/dist/test/suites/build/electron-targets.test.js +92 -0
- package/dist/test/suites/build/ensure-target.test.js +131 -0
- package/dist/test/suites/build/entitlements.test.js +107 -0
- package/dist/test/suites/build/env-delivery.test.js +166 -0
- package/dist/test/suites/build/env-watch.test.js +86 -0
- package/dist/test/suites/build/exports.test.js +63 -0
- package/dist/test/suites/build/finalize-release.test.js +152 -0
- package/dist/test/suites/build/format-fetch-error.test.js +85 -0
- package/dist/test/suites/build/framework-deps.test.js +173 -0
- package/dist/test/suites/build/get-config.test.js +137 -0
- package/dist/test/suites/build/github-utils.test.js +54 -0
- package/dist/test/suites/build/icon-sheet.test.js +62 -0
- package/dist/test/suites/build/license-stamp.test.js +63 -0
- package/dist/test/suites/build/logger-lite.test.js +148 -0
- package/dist/test/suites/build/logs-command.test.js +226 -0
- package/dist/test/suites/build/manager.test.js +193 -0
- package/dist/test/suites/build/menu-mixin.test.js +130 -0
- package/dist/test/suites/build/merge-line-files.test.js +230 -0
- package/dist/test/suites/build/omega-shell.test.js +94 -0
- package/dist/test/suites/build/omega-tokens.test.js +64 -0
- package/dist/test/suites/build/project-scripts-deps.test.js +90 -0
- package/dist/test/suites/build/push-secrets.test.js +226 -0
- package/dist/test/suites/build/release-pipeline.test.js +365 -0
- package/dist/test/suites/build/renderer-auth-bridge.test.js +158 -0
- package/dist/test/suites/build/renderer-entry-key.test.js +37 -0
- package/dist/test/suites/build/renderer-node-shims.test.js +125 -0
- package/dist/test/suites/build/resolve-icons.test.js +296 -0
- package/dist/test/suites/build/resolve-signing-cert.test.js +342 -0
- package/dist/test/suites/build/restart-manager-install.test.js +178 -0
- package/dist/test/suites/build/restart-manager-protocol.test.js +158 -0
- package/dist/test/suites/build/run-consumer-hook.test.js +137 -0
- package/dist/test/suites/build/runner.test.js +2599 -0
- package/dist/test/suites/build/sanitize-signing-env.test.js +98 -0
- package/dist/test/suites/build/sanitize-url.test.js +78 -0
- package/dist/test/suites/build/secure-remote-url.test.js +42 -0
- package/dist/test/suites/build/sentry.test.js +89 -0
- package/dist/test/suites/build/serve-resolve-electron.test.js +58 -0
- package/dist/test/suites/build/setup-scripts.test.js +172 -0
- package/dist/test/suites/build/sign-events.test.js +90 -0
- package/dist/test/suites/build/sign-windows-e2e.test.js +302 -0
- package/dist/test/suites/build/sign-windows.test.js +313 -0
- package/dist/test/suites/build/strip-dev-blocks.test.js +116 -0
- package/dist/test/suites/build/templating.test.js +95 -0
- package/dist/test/suites/build/test-discovery.test.js +64 -0
- package/dist/test/suites/build/test-stealth.test.js +83 -0
- package/dist/test/suites/build/test-target-no-match.test.js +119 -0
- package/dist/test/suites/build/update-info.test.js +224 -0
- package/dist/test/suites/build/url-helpers.test.js +289 -0
- package/dist/test/suites/build/validate-certs.test.js +103 -0
- package/dist/test/suites/build/validate-config.test.js +90 -0
- package/dist/test/suites/build/verb-logs.test.js +43 -0
- package/dist/test/suites/build/wave5-pins.test.js +35 -0
- package/dist/test/suites/main/analytics.test.js +364 -0
- package/dist/test/suites/main/app-state.test.js +197 -0
- package/dist/test/suites/main/auth-flow.test.js +196 -0
- package/dist/test/suites/main/auth-persistence.test.js +167 -0
- package/dist/test/suites/main/auto-updater.test.js +1138 -0
- package/dist/test/suites/main/boot-sequence.test.js +77 -0
- package/dist/test/suites/main/client-bridge.integration.test.js +134 -0
- package/dist/test/suites/main/client-bridge.test.js +249 -0
- package/dist/test/suites/main/context-menu.test.js +365 -0
- package/dist/test/suites/main/context.test.js +155 -0
- package/dist/test/suites/main/deep-link.test.js +288 -0
- package/dist/test/suites/main/epipe-handler.test.js +40 -0
- package/dist/test/suites/main/fontawesome.test.js +147 -0
- package/dist/test/suites/main/harness-cdp.test.js +49 -0
- package/dist/test/suites/main/ipc.test.js +165 -0
- package/dist/test/suites/main/menu.test.js +419 -0
- package/dist/test/suites/main/protocol.test.js +77 -0
- package/dist/test/suites/main/remote-config.test.js +189 -0
- package/dist/test/suites/main/remote-scripts.test.js +276 -0
- package/dist/test/suites/main/restart-manager.test.js +328 -0
- package/dist/test/suites/main/startup-paths-and-ua.test.js +99 -0
- package/dist/test/suites/main/startup.test.js +129 -0
- package/dist/test/suites/main/stealth-window.test.js +74 -0
- package/dist/test/suites/main/storage.test.js +89 -0
- package/dist/test/suites/main/theme.test.js +137 -0
- package/dist/test/suites/main/tray.test.js +322 -0
- package/dist/test/suites/main/truncate-on-boot.test.js +22 -0
- package/dist/test/suites/main/url-helpers.test.js +429 -0
- package/dist/test/suites/main/usage.test.js +127 -0
- package/dist/test/suites/main/window-bounds.test.js +169 -0
- package/dist/test/suites/main/window-manager.test.js +233 -0
- package/dist/test/suites/renderer/analytics-bridge.test.js +128 -0
- package/dist/test/suites/renderer/cross-context-helpers.test.js +131 -0
- package/dist/test/suites/renderer/fontawesome.test.js +190 -0
- package/dist/test/suites/renderer/round-trip.test.js +107 -0
- package/dist/test/suites/renderer/theme.test.js +118 -0
- package/dist/test/suites/renderer/tooltips.test.js +166 -0
- package/dist/test/suites/renderer/verts.test.js +100 -0
- package/dist/test/suites/renderer/window-em-surface.test.js +107 -0
- package/dist/test/utils/dist-snapshot.js +36 -0
- package/dist/test/utils/extended-mode-warning.js +13 -0
- package/dist/utils/app-root.js +28 -0
- package/dist/utils/attach-log-file.js +4 -0
- package/dist/utils/build-pipeline.js +58 -0
- package/dist/utils/deliver-certs.js +69 -0
- package/dist/utils/derive-signing-env.js +56 -0
- package/dist/utils/dist-root.js +21 -0
- package/dist/utils/electron-node-version.js +48 -0
- package/dist/utils/electron-targets.js +72 -0
- package/dist/utils/format-fetch-error.js +38 -0
- package/dist/utils/github.js +73 -0
- package/dist/utils/load-consumer-file.js +26 -0
- package/dist/utils/merge-line-files.js +5 -0
- package/dist/utils/mode-helpers.js +147 -0
- package/dist/utils/resolve-signing-cert.js +175 -0
- package/dist/utils/run-consumer-hook.js +38 -0
- package/dist/utils/runner-env.js +687 -0
- package/dist/utils/safe-install.js +4 -0
- package/dist/utils/sanitize-signing-env.js +45 -0
- package/dist/utils/sanitize-url.js +28 -0
- package/dist/utils/secure-remote-url.js +25 -0
- package/dist/utils/stealth-window.js +37 -0
- package/dist/utils/test-stealth.js +29 -0
- package/dist/utils/url-helpers.js +169 -0
- package/dist/vendor/account/engine.js +182 -0
- package/dist/vendor/account/features.js +220 -0
- package/dist/vendor/account/index.js +53 -0
- package/dist/vendor/account/schema.js +272 -0
- package/dist/vendor/account/subscription.js +38 -0
- package/dist/vendor/analytics/adapters/ga4.js +26 -0
- package/dist/vendor/analytics/adapters/meta.js +26 -0
- package/dist/vendor/analytics/adapters/resolve.js +130 -0
- package/dist/vendor/analytics/adapters/tiktok.js +27 -0
- package/dist/vendor/analytics/catalog.js +908 -0
- package/dist/vendor/analytics/consent.js +49 -0
- package/dist/vendor/analytics/core.js +141 -0
- package/dist/vendor/analytics/identity.js +136 -0
- package/dist/vendor/analytics/index.js +170 -0
- package/dist/vendor/analytics/logger.js +40 -0
- package/dist/vendor/analytics/transports/browser.js +110 -0
- package/dist/vendor/config/company.js +31 -0
- package/dist/vendor/config/defaults.js +173 -0
- package/dist/vendor/config/demo.js +18 -0
- package/dist/vendor/config/desktop-artifacts.js +110 -0
- package/dist/vendor/config/edit.js +769 -0
- package/dist/vendor/config/env-delivery.js +145 -0
- package/dist/vendor/config/env-rules.js +93 -0
- package/dist/vendor/config/env-schema.js +1078 -0
- package/dist/vendor/config/env.js +445 -0
- package/dist/vendor/config/hooks.js +97 -0
- package/dist/vendor/config/index.js +237 -0
- package/dist/vendor/config/instances.js +208 -0
- package/dist/vendor/config/load.js +490 -0
- package/dist/vendor/config/merge.js +68 -0
- package/dist/vendor/config/order.js +139 -0
- package/dist/vendor/config/ports.js +374 -0
- package/dist/vendor/config/providers.js +32 -0
- package/dist/vendor/config/repo.js +142 -0
- package/dist/vendor/config/retired-keys.js +430 -0
- package/dist/vendor/config/schema.js +1610 -0
- package/dist/vendor/config/secrets.js +50 -0
- package/dist/vendor/config/seed.js +34 -0
- package/dist/vendor/config/site-global.js +205 -0
- package/dist/vendor/config/validate.js +554 -0
- package/dist/vendor/config/winback.js +61 -0
- package/dist/vendor/devkit/actions-secrets.js +145 -0
- package/dist/vendor/devkit/attach-log-file.js +262 -0
- package/dist/vendor/devkit/bundle.js +292 -0
- package/dist/vendor/devkit/certs.js +199 -0
- package/dist/vendor/devkit/ci-workflows.js +520 -0
- package/dist/vendor/devkit/clean-dirs.js +28 -0
- package/dist/vendor/devkit/cli-router.js +156 -0
- package/dist/vendor/devkit/defaults-engine.js +372 -0
- package/dist/vendor/devkit/deploy-precheck.js +54 -0
- package/dist/vendor/devkit/deploy-record.js +180 -0
- package/dist/vendor/devkit/deploy.js +286 -0
- package/dist/vendor/devkit/empty-modules-plugin.js +64 -0
- package/dist/vendor/devkit/env-watch.js +132 -0
- package/dist/vendor/devkit/framework-deps.js +46 -0
- package/dist/vendor/devkit/license.js +155 -0
- package/dist/vendor/devkit/local.js +1905 -0
- package/dist/vendor/devkit/logger.js +128 -0
- package/dist/vendor/devkit/merge-line-files.js +296 -0
- package/dist/vendor/devkit/npm-registry.js +52 -0
- package/dist/vendor/devkit/omega-bin.js +345 -0
- package/dist/vendor/devkit/prompt.js +187 -0
- package/dist/vendor/devkit/safe-install.js +18 -0
- package/dist/vendor/devkit/scaffold-guard.js +96 -0
- package/dist/vendor/devkit/stop-signals.js +28 -0
- package/dist/vendor/devkit/strip-dev-blocks-plugin.js +29 -0
- package/dist/vendor/devkit/strip-dev-blocks.js +32 -0
- package/dist/vendor/devkit/target-dependencies.js +137 -0
- package/dist/vendor/devkit/target-secrets.js +165 -0
- package/dist/vendor/devkit/test/assert.js +120 -0
- package/dist/vendor/devkit/test/define-cases.js +104 -0
- package/dist/vendor/devkit/test/extended-mode-warning.js +22 -0
- package/dist/vendor/devkit/test/runner-core.js +554 -0
- package/dist/vendor/devkit/test/scope.js +162 -0
- package/dist/vendor/devkit/update.js +569 -0
- package/dist/vendor/monitoring/core.js +180 -0
- package/dist/vendor/monitoring/env.js +49 -0
- package/dist/vendor/monitoring/index.js +28 -0
- package/dist/vendor/monitoring/logger.js +39 -0
- package/dist/vendor/monitoring/main.js +99 -0
- package/dist/vendor/monitoring/preload.js +45 -0
- package/dist/vendor/monitoring/renderer.js +87 -0
- package/docs/analytics.md +140 -0
- package/docs/app-state.md +92 -0
- package/docs/audit.md +69 -0
- package/docs/auto-updater.md +243 -0
- package/docs/boot-sequence.md +39 -0
- package/docs/build-system.md +161 -0
- package/docs/cdp-debugging.md +169 -0
- package/docs/client-bridge.md +267 -0
- package/docs/common-mistakes.md +21 -0
- package/docs/config-schema.md +120 -0
- package/docs/context-menu.md +112 -0
- package/docs/context.md +81 -0
- package/docs/css.md +84 -0
- package/docs/deep-link.md +178 -0
- package/docs/environment-detection.md +104 -0
- package/docs/fontawesome.md +107 -0
- package/docs/hooks.md +87 -0
- package/docs/icons.md +79 -0
- package/docs/index.md +279 -0
- package/docs/installer-options.md +162 -0
- package/docs/ipc.md +61 -0
- package/docs/lib-modules.md +53 -0
- package/docs/logging.md +229 -0
- package/docs/menu.md +160 -0
- package/docs/releasing.md +229 -0
- package/docs/remote-config.md +118 -0
- package/docs/remote-scripts.md +144 -0
- package/docs/restart-manager.md +144 -0
- package/docs/runner.md +253 -0
- package/docs/sentry.md +97 -0
- package/docs/shared/agent-docs.md +89 -0
- package/docs/shared/analytics.md +612 -0
- package/docs/shared/brands.md +51 -0
- package/docs/shared/breaking-changes.md +497 -0
- package/docs/shared/config.md +1387 -0
- package/docs/shared/deploys.md +215 -0
- package/docs/shared/icons.md +201 -0
- package/docs/shared/local-dev.md +147 -0
- package/docs/shared/logging.md +202 -0
- package/docs/shared/monitoring.md +153 -0
- package/docs/shared/publishing.md +183 -0
- package/docs/shared/rulings.md +34 -0
- package/docs/shared/testing.md +147 -0
- package/docs/shared/theming.md +604 -0
- package/docs/shared/translation.md +291 -0
- package/docs/shared/updates.md +61 -0
- package/docs/signing.md +267 -0
- package/docs/startup.md +142 -0
- package/docs/storage.md +59 -0
- package/docs/templating.md +101 -0
- package/docs/test-boot-layer.md +157 -0
- package/docs/test-framework.md +357 -0
- package/docs/themes.md +135 -0
- package/docs/tooltips.md +99 -0
- package/docs/tray.md +164 -0
- package/docs/usage.md +58 -0
- package/docs/verts.md +62 -0
- package/docs/windows.md +149 -0
- package/package.json +184 -0
- package/scripts/sync-nvmrc.js +100 -0
|
@@ -0,0 +1,1138 @@
|
|
|
1
|
+
// Main-process tests for lib/auto-updater.js — state machine, dev simulation,
|
|
2
|
+
// 30-day pending-update gate (first-download-wins).
|
|
3
|
+
//
|
|
4
|
+
// Each test resets the updater + clears storage so we get clean state.
|
|
5
|
+
|
|
6
|
+
const defineCases = require('../../../vendor/devkit/test/define-cases.js');
|
|
7
|
+
|
|
8
|
+
const STORAGE_KEY = 'autoUpdater';
|
|
9
|
+
|
|
10
|
+
async function reinit(ctx, env) {
|
|
11
|
+
const updater = ctx.manager.autoUpdater;
|
|
12
|
+
updater.shutdown();
|
|
13
|
+
ctx.manager.storage.set(STORAGE_KEY, null);
|
|
14
|
+
|
|
15
|
+
// Snapshot + override env for this run.
|
|
16
|
+
const saved = { OMEGA_DEV_UPDATE: process.env.OMEGA_DEV_UPDATE };
|
|
17
|
+
for (const [k, v] of Object.entries(env || {})) {
|
|
18
|
+
if (v == null) delete process.env[k];
|
|
19
|
+
else process.env[k] = v;
|
|
20
|
+
}
|
|
21
|
+
await updater.initialize(ctx.manager);
|
|
22
|
+
return async () => {
|
|
23
|
+
updater.shutdown();
|
|
24
|
+
for (const [k, v] of Object.entries(saved)) {
|
|
25
|
+
if (v == null) delete process.env[k];
|
|
26
|
+
else process.env[k] = v;
|
|
27
|
+
}
|
|
28
|
+
// Re-init with the original env so subsequent tests have a working updater + IPC handlers.
|
|
29
|
+
await updater.initialize(ctx.manager);
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function waitFor(predicate, { timeout = 3000, step = 50 } = {}) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const start = Date.now();
|
|
36
|
+
const tick = () => {
|
|
37
|
+
try {
|
|
38
|
+
if (predicate()) return resolve();
|
|
39
|
+
} catch (e) { /* keep polling */ }
|
|
40
|
+
if (Date.now() - start > timeout) return reject(new Error('waitFor timeout'));
|
|
41
|
+
setTimeout(tick, step);
|
|
42
|
+
};
|
|
43
|
+
tick();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = defineCases({
|
|
48
|
+
type: 'suite',
|
|
49
|
+
layer: 'main',
|
|
50
|
+
description: 'auto-updater (main)',
|
|
51
|
+
cleanup: async (ctx) => {
|
|
52
|
+
ctx.manager.autoUpdater.shutdown();
|
|
53
|
+
ctx.manager.storage.set(STORAGE_KEY, null);
|
|
54
|
+
delete process.env.OMEGA_DEV_UPDATE;
|
|
55
|
+
await ctx.manager.autoUpdater.initialize(ctx.manager);
|
|
56
|
+
},
|
|
57
|
+
tests: [
|
|
58
|
+
{
|
|
59
|
+
name: 'initialize ran during boot — initial status is idle',
|
|
60
|
+
run: (ctx) => {
|
|
61
|
+
const status = ctx.manager.autoUpdater.getStatus();
|
|
62
|
+
ctx.expect(status.code).toBe('idle');
|
|
63
|
+
ctx.expect(status.percent).toBe(0);
|
|
64
|
+
ctx.expect(status.error).toBe(null);
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: 'dev simulation: available scenario walks state through downloaded',
|
|
69
|
+
run: async (ctx) => {
|
|
70
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
|
|
71
|
+
try {
|
|
72
|
+
await ctx.manager.autoUpdater.checkNow({ userInitiated: true });
|
|
73
|
+
await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'downloaded', { timeout: 5000 });
|
|
74
|
+
|
|
75
|
+
const s = ctx.manager.autoUpdater.getStatus();
|
|
76
|
+
ctx.expect(s.code).toBe('downloaded');
|
|
77
|
+
ctx.expect(s.version).toBe('999.0.0');
|
|
78
|
+
ctx.expect(s.percent).toBe(100);
|
|
79
|
+
ctx.expect(typeof s.downloadedAt).toBe('number');
|
|
80
|
+
} finally { await restore(); }
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'dev simulation: unavailable scenario lands in not-available',
|
|
85
|
+
run: async (ctx) => {
|
|
86
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'unavailable' });
|
|
87
|
+
try {
|
|
88
|
+
await ctx.manager.autoUpdater.checkNow({ userInitiated: false });
|
|
89
|
+
await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'not-available');
|
|
90
|
+
|
|
91
|
+
const s = ctx.manager.autoUpdater.getStatus();
|
|
92
|
+
ctx.expect(s.code).toBe('not-available');
|
|
93
|
+
ctx.expect(s.error).toBe(null);
|
|
94
|
+
} finally { await restore(); }
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'dev simulation: error scenario lands in error',
|
|
99
|
+
run: async (ctx) => {
|
|
100
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'error' });
|
|
101
|
+
try {
|
|
102
|
+
await ctx.manager.autoUpdater.checkNow({ userInitiated: false });
|
|
103
|
+
await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'error');
|
|
104
|
+
|
|
105
|
+
const s = ctx.manager.autoUpdater.getStatus();
|
|
106
|
+
ctx.expect(s.code).toBe('error');
|
|
107
|
+
ctx.expect(s.error).toBeDefined();
|
|
108
|
+
ctx.expect(s.error.message).toMatch(/Simulated/);
|
|
109
|
+
} finally { await restore(); }
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'simulate(): available scenario walks state through downloaded with no env var set',
|
|
114
|
+
run: async (ctx) => {
|
|
115
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
116
|
+
try {
|
|
117
|
+
await ctx.manager.autoUpdater.simulate('available');
|
|
118
|
+
await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'downloaded', { timeout: 5000 });
|
|
119
|
+
|
|
120
|
+
const s = ctx.manager.autoUpdater.getStatus();
|
|
121
|
+
ctx.expect(s.code).toBe('downloaded');
|
|
122
|
+
ctx.expect(s.version).toBe('999.0.0');
|
|
123
|
+
ctx.expect(s.percent).toBe(100);
|
|
124
|
+
} finally { await restore(); }
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'simulate(): unavailable scenario lands in not-available',
|
|
129
|
+
run: async (ctx) => {
|
|
130
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
131
|
+
try {
|
|
132
|
+
await ctx.manager.autoUpdater.simulate('unavailable');
|
|
133
|
+
await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'not-available');
|
|
134
|
+
|
|
135
|
+
ctx.expect(ctx.manager.autoUpdater.getStatus().error).toBe(null);
|
|
136
|
+
} finally { await restore(); }
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: 'simulate(): error scenario lands in error',
|
|
141
|
+
run: async (ctx) => {
|
|
142
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
143
|
+
try {
|
|
144
|
+
await ctx.manager.autoUpdater.simulate('error');
|
|
145
|
+
await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'error');
|
|
146
|
+
|
|
147
|
+
ctx.expect(ctx.manager.autoUpdater.getStatus().error.message).toMatch(/Simulated/);
|
|
148
|
+
} finally { await restore(); }
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: 'simulate(): unknown scenario throws',
|
|
153
|
+
run: async (ctx) => {
|
|
154
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
155
|
+
try {
|
|
156
|
+
await ctx.expect(() => ctx.manager.autoUpdater.simulate('sideways')).toThrow(/unknown scenario/i);
|
|
157
|
+
await ctx.expect(() => ctx.manager.autoUpdater.simulate()).toThrow(/scenario required/i);
|
|
158
|
+
} finally { await restore(); }
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'simulate(): refuses in a production build unless OMEGA_DEV_UPDATE is set',
|
|
163
|
+
run: async (ctx) => {
|
|
164
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
165
|
+
const origIsProduction = ctx.manager.isProduction;
|
|
166
|
+
ctx.manager.isProduction = () => true;
|
|
167
|
+
try {
|
|
168
|
+
await ctx.expect(() => ctx.manager.autoUpdater.simulate('available')).toThrow(/production/i);
|
|
169
|
+
ctx.expect(ctx.manager.autoUpdater.getStatus().code).toBe('idle');
|
|
170
|
+
|
|
171
|
+
// ...but a packaged QA build launched with the env var set may still simulate.
|
|
172
|
+
process.env.OMEGA_DEV_UPDATE = 'available';
|
|
173
|
+
await ctx.manager.autoUpdater.simulate('unavailable');
|
|
174
|
+
await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'not-available');
|
|
175
|
+
} finally {
|
|
176
|
+
delete process.env.OMEGA_DEV_UPDATE;
|
|
177
|
+
ctx.manager.isProduction = origIsProduction;
|
|
178
|
+
await restore();
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
// The whole point of the session flag: simulate() leaves the synthetic library
|
|
184
|
+
// wired, so every LATER trigger (the hourly feed tick) drives the simulator too.
|
|
185
|
+
// Without _isSimulating() latching, that fake 'downloaded' walks into the real
|
|
186
|
+
// install path and flips the manager's quit latch.
|
|
187
|
+
name: 'simulate(): latches the session so a later feed tick cannot reach the real install path',
|
|
188
|
+
run: async (ctx) => {
|
|
189
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
190
|
+
const u = ctx.manager.autoUpdater;
|
|
191
|
+
const origPrompt = u._promptToInstall;
|
|
192
|
+
let promptCalls = 0;
|
|
193
|
+
u._promptToInstall = async () => { promptCalls++; };
|
|
194
|
+
try {
|
|
195
|
+
await u.simulate('unavailable');
|
|
196
|
+
await waitFor(() => u.getStatus().code === 'not-available');
|
|
197
|
+
ctx.expect(u._isSimulating()).toBe(true);
|
|
198
|
+
|
|
199
|
+
// The feed tick calls checkForUpdates() with no scenario: the simulator falls
|
|
200
|
+
// back to 'available' and lands on a fake downloaded v999.0.0.
|
|
201
|
+
await u._feedCheckTick();
|
|
202
|
+
await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
|
|
203
|
+
|
|
204
|
+
// User idle well past the threshold, background check: only the simulation
|
|
205
|
+
// guard stands between this and a real install.
|
|
206
|
+
u._userInitiated = false;
|
|
207
|
+
u._lastActivityAt = Date.now() - (10 * 60 * 1000);
|
|
208
|
+
u._evaluateIdleInstall();
|
|
209
|
+
|
|
210
|
+
ctx.expect(ctx.manager._allowQuit).toBe(false);
|
|
211
|
+
ctx.expect(promptCalls).toBe(0);
|
|
212
|
+
} finally {
|
|
213
|
+
u._promptToInstall = origPrompt;
|
|
214
|
+
ctx.manager._allowQuit = false;
|
|
215
|
+
await restore();
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: 'simulate(): shutdown clears the session flag',
|
|
221
|
+
run: async (ctx) => {
|
|
222
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
223
|
+
const u = ctx.manager.autoUpdater;
|
|
224
|
+
try {
|
|
225
|
+
await u.simulate('unavailable');
|
|
226
|
+
ctx.expect(u._isSimulating()).toBe(true);
|
|
227
|
+
|
|
228
|
+
u.shutdown();
|
|
229
|
+
ctx.expect(u._isSimulating()).toBe(false);
|
|
230
|
+
} finally { await restore(); }
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'simulate(): refuses while a cascade is already running',
|
|
235
|
+
run: async (ctx) => {
|
|
236
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
237
|
+
const u = ctx.manager.autoUpdater;
|
|
238
|
+
try {
|
|
239
|
+
await u.simulate('available');
|
|
240
|
+
ctx.expect(u._devSimulating).toBe(true);
|
|
241
|
+
|
|
242
|
+
await ctx.expect(() => u.simulate('error')).toThrow(/already running/i);
|
|
243
|
+
// The refused call must not have reset the running cascade's state.
|
|
244
|
+
ctx.expect(u.getStatus().code).not.toBe('idle');
|
|
245
|
+
await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
|
|
246
|
+
} finally { await restore(); }
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: 'dev simulation: the env-var cascade never writes the pendingUpdate storage key',
|
|
251
|
+
run: async (ctx) => {
|
|
252
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
|
|
253
|
+
try {
|
|
254
|
+
await ctx.manager.autoUpdater.checkNow({ userInitiated: false });
|
|
255
|
+
await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'downloaded', { timeout: 5000 });
|
|
256
|
+
|
|
257
|
+
// The fake download stamps memory (the in-session UI flow reads the
|
|
258
|
+
// same as a real one) but must leave the production record alone.
|
|
259
|
+
ctx.expect(typeof ctx.manager.autoUpdater.getStatus().downloadedAt).toBe('number');
|
|
260
|
+
ctx.expect(ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`)).toBeUndefined();
|
|
261
|
+
} finally { await restore(); }
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
name: 'simulate(): the cascade never writes the pendingUpdate storage key',
|
|
266
|
+
run: async (ctx) => {
|
|
267
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
268
|
+
try {
|
|
269
|
+
await ctx.manager.autoUpdater.simulate('available');
|
|
270
|
+
await waitFor(() => ctx.manager.autoUpdater.getStatus().code === 'downloaded', { timeout: 5000 });
|
|
271
|
+
|
|
272
|
+
ctx.expect(typeof ctx.manager.autoUpdater.getStatus().downloadedAt).toBe('number');
|
|
273
|
+
ctx.expect(ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`)).toBeUndefined();
|
|
274
|
+
} finally { await restore(); }
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
// Heals profiles an older build already polluted: a simulated record
|
|
279
|
+
// whose version can never be applied would otherwise seed the 30-day
|
|
280
|
+
// gate and force-install the first REAL download on the spot.
|
|
281
|
+
name: 'a stale simulated pendingUpdate cannot seed a real download (30-day gate does not force install)',
|
|
282
|
+
run: async (ctx) => {
|
|
283
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
284
|
+
const updater = ctx.manager.autoUpdater;
|
|
285
|
+
let quitCalled = false;
|
|
286
|
+
let origQuit;
|
|
287
|
+
try {
|
|
288
|
+
// A 40-day-old simulator record, then a boot so the reconciler sees it.
|
|
289
|
+
const staleTs = Date.now() - (40 * 24 * 60 * 60 * 1000);
|
|
290
|
+
ctx.manager.storage.set(`${STORAGE_KEY}.pendingUpdate`, { version: '999.0.0', downloadedAt: staleTs });
|
|
291
|
+
updater.shutdown();
|
|
292
|
+
await updater.initialize(ctx.manager);
|
|
293
|
+
|
|
294
|
+
ctx.expect(ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`)).toBe(null);
|
|
295
|
+
ctx.expect(updater._state.downloadedAt).toBe(null);
|
|
296
|
+
|
|
297
|
+
// A real update downloads: it starts its OWN 30-day clock.
|
|
298
|
+
const before = Date.now();
|
|
299
|
+
origQuit = updater._library.quitAndInstall;
|
|
300
|
+
updater._library.quitAndInstall = () => { quitCalled = true; };
|
|
301
|
+
updater._library.emit('update-downloaded', { version: '2.0.0' });
|
|
302
|
+
|
|
303
|
+
const stored = ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`);
|
|
304
|
+
ctx.expect(stored.version).toBe('2.0.0');
|
|
305
|
+
ctx.expect(stored.downloadedAt).not.toBeLessThan(before);
|
|
306
|
+
ctx.expect(updater._state.downloadedAt).toBe(stored.downloadedAt);
|
|
307
|
+
ctx.expect(quitCalled).toBe(false);
|
|
308
|
+
} finally {
|
|
309
|
+
// The spy sat on the shared electron-updater singleton.
|
|
310
|
+
if (origQuit) updater._library.quitAndInstall = origQuit;
|
|
311
|
+
ctx.manager._allowQuit = false;
|
|
312
|
+
await restore();
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
// A REAL download, not the simulator: the simulator deliberately never
|
|
318
|
+
// writes this key (see the two cases above).
|
|
319
|
+
name: 'first download persists pendingUpdate to storage',
|
|
320
|
+
run: async (ctx) => {
|
|
321
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
322
|
+
const updater = ctx.manager.autoUpdater;
|
|
323
|
+
try {
|
|
324
|
+
updater._library.emit('update-downloaded', { version: '2.0.0' });
|
|
325
|
+
ctx.expect(updater.getStatus().code).toBe('downloaded');
|
|
326
|
+
|
|
327
|
+
const stored = ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`);
|
|
328
|
+
ctx.expect(stored).toBeDefined();
|
|
329
|
+
ctx.expect(stored.version).toBe('2.0.0');
|
|
330
|
+
ctx.expect(typeof stored.downloadedAt).toBe('number');
|
|
331
|
+
} finally { await restore(); }
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: 'subsequent download keeps downloadedAt but tracks the NEW version (first timer wins, clear-on-apply matches)',
|
|
336
|
+
run: async (ctx) => {
|
|
337
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
338
|
+
try {
|
|
339
|
+
// Plant an older pending-update record manually.
|
|
340
|
+
const oldTs = Date.now() - (10 * 24 * 60 * 60 * 1000); // 10 days ago
|
|
341
|
+
ctx.manager.storage.set(`${STORAGE_KEY}.pendingUpdate`, { version: '888.0.0', downloadedAt: oldTs });
|
|
342
|
+
|
|
343
|
+
// Call _recordDownloadedAt for a "new" download.
|
|
344
|
+
ctx.manager.autoUpdater._recordDownloadedAt('2.0.0');
|
|
345
|
+
|
|
346
|
+
const stored = ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`);
|
|
347
|
+
// Timer unchanged (first-download-wins) — but the version must track
|
|
348
|
+
// the newest download: after installing 2.0.0, the clear-on-apply
|
|
349
|
+
// check (pending.version === getVersion()) has to match, or the stale
|
|
350
|
+
// flag would force-install every future download instantly forever.
|
|
351
|
+
ctx.expect(stored.downloadedAt).toBe(oldTs);
|
|
352
|
+
ctx.expect(stored.version).toBe('2.0.0');
|
|
353
|
+
} finally { await restore(); }
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: '30-day gate: pending update older than maxAgeMs forces installNow',
|
|
358
|
+
run: async (ctx) => {
|
|
359
|
+
const restore = await reinit(ctx, {});
|
|
360
|
+
let installCalled = false;
|
|
361
|
+
try {
|
|
362
|
+
// Override installNow to capture the trigger without actually quitting.
|
|
363
|
+
const origInstall = ctx.manager.autoUpdater.installNow;
|
|
364
|
+
ctx.manager.autoUpdater.installNow = async () => { installCalled = true; return true; };
|
|
365
|
+
|
|
366
|
+
// Plant an old pending-update.
|
|
367
|
+
const oldTs = Date.now() - (31 * 24 * 60 * 60 * 1000);
|
|
368
|
+
ctx.manager.autoUpdater._state.downloadedAt = oldTs;
|
|
369
|
+
ctx.manager.autoUpdater._options.maxAgeMs = 30 * 24 * 60 * 60 * 1000;
|
|
370
|
+
|
|
371
|
+
const triggered = ctx.manager.autoUpdater._enforceMaxAgeGate();
|
|
372
|
+
ctx.expect(triggered).toBe(true);
|
|
373
|
+
ctx.expect(installCalled).toBe(true);
|
|
374
|
+
|
|
375
|
+
ctx.manager.autoUpdater.installNow = origInstall;
|
|
376
|
+
} finally { await restore(); }
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
// wave-5 F3: at reconcile the gate provably CANNOT install (state is
|
|
381
|
+
// 'idle' and installNow requires 'downloaded') — the install must fire
|
|
382
|
+
// when the startup re-download lands, via the update-downloaded handler.
|
|
383
|
+
name: '30-day gate: carried-over stale pending installs when update-downloaded fires (real installNow state guard)',
|
|
384
|
+
run: async (ctx) => {
|
|
385
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: null });
|
|
386
|
+
const updater = ctx.manager.autoUpdater;
|
|
387
|
+
let quitCalled = false;
|
|
388
|
+
let origQuit;
|
|
389
|
+
try {
|
|
390
|
+
// Plant a >30-day pending update, then reconcile like a fresh boot.
|
|
391
|
+
const oldTs = Date.now() - (31 * 24 * 60 * 60 * 1000);
|
|
392
|
+
ctx.manager.storage.set(`${STORAGE_KEY}.pendingUpdate`, { version: '777.0.0', downloadedAt: oldTs });
|
|
393
|
+
updater._options.maxAgeMs = 30 * 24 * 60 * 60 * 1000;
|
|
394
|
+
updater._reconcilePendingUpdate();
|
|
395
|
+
|
|
396
|
+
// Reconcile alone cannot install — the REAL installNow refuses on 'idle'.
|
|
397
|
+
ctx.expect(updater._state.code).toBe('idle');
|
|
398
|
+
ctx.expect(await updater.installNow()).toBe(false);
|
|
399
|
+
|
|
400
|
+
// The startup re-download lands: the wired update-downloaded handler
|
|
401
|
+
// must enforce the gate against the restored ORIGINAL downloadedAt.
|
|
402
|
+
origQuit = updater._library.quitAndInstall;
|
|
403
|
+
updater._library.quitAndInstall = () => { quitCalled = true; };
|
|
404
|
+
updater._library.emit('update-downloaded', { version: '777.0.0' });
|
|
405
|
+
ctx.expect(updater._state.code).toBe('downloaded');
|
|
406
|
+
ctx.expect(updater._state.downloadedAt).toBe(oldTs); // first-download-wins
|
|
407
|
+
ctx.expect(quitCalled).toBe(true);
|
|
408
|
+
} finally {
|
|
409
|
+
// The spy sat on the shared electron-updater singleton; the install
|
|
410
|
+
// path also flipped the manager's quit latch — undo both.
|
|
411
|
+
if (origQuit) updater._library.quitAndInstall = origQuit;
|
|
412
|
+
ctx.manager._allowQuit = false;
|
|
413
|
+
await restore();
|
|
414
|
+
}
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
{
|
|
418
|
+
name: '30-day gate: fresh pending update does NOT force install',
|
|
419
|
+
run: async (ctx) => {
|
|
420
|
+
const restore = await reinit(ctx, {});
|
|
421
|
+
try {
|
|
422
|
+
ctx.manager.autoUpdater._state.downloadedAt = Date.now() - (5 * 24 * 60 * 60 * 1000);
|
|
423
|
+
ctx.manager.autoUpdater._options.maxAgeMs = 30 * 24 * 60 * 60 * 1000;
|
|
424
|
+
|
|
425
|
+
const triggered = ctx.manager.autoUpdater._enforceMaxAgeGate();
|
|
426
|
+
ctx.expect(triggered).toBe(false);
|
|
427
|
+
} finally { await restore(); }
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
name: 'pendingUpdate cleared when current version matches (update was applied)',
|
|
432
|
+
run: async (ctx) => {
|
|
433
|
+
const restore = await reinit(ctx, {});
|
|
434
|
+
try {
|
|
435
|
+
// Stash a pending-update entry whose version matches our current package version.
|
|
436
|
+
const currentVersion = ctx.manager.getVersion();
|
|
437
|
+
ctx.expect(typeof currentVersion).toBe('string');
|
|
438
|
+
|
|
439
|
+
ctx.manager.storage.set(`${STORAGE_KEY}.pendingUpdate`, {
|
|
440
|
+
version: currentVersion,
|
|
441
|
+
downloadedAt: Date.now() - (5 * 24 * 60 * 60 * 1000),
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// Re-init triggers the reconciler.
|
|
445
|
+
ctx.manager.autoUpdater.shutdown();
|
|
446
|
+
await ctx.manager.autoUpdater.initialize(ctx.manager);
|
|
447
|
+
|
|
448
|
+
const stillThere = ctx.manager.storage.get(`${STORAGE_KEY}.pendingUpdate`);
|
|
449
|
+
ctx.expect(stillThere).toBe(null);
|
|
450
|
+
} finally { await restore(); }
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
name: 'checkNow returns current status object',
|
|
455
|
+
run: async (ctx) => {
|
|
456
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'unavailable' });
|
|
457
|
+
try {
|
|
458
|
+
const result = await ctx.manager.autoUpdater.checkNow();
|
|
459
|
+
ctx.expect(typeof result.code).toBe('string');
|
|
460
|
+
ctx.expect('percent' in result).toBe(true);
|
|
461
|
+
} finally { await restore(); }
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: 'IPC handler registered: desktop:auto-updater:status returns status',
|
|
466
|
+
run: async (ctx) => {
|
|
467
|
+
const result = await ctx.manager.ipc.invoke('desktop:auto-updater:status');
|
|
468
|
+
ctx.expect(typeof result.code).toBe('string');
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
name: 'enabled=false: skip wiring, no library, no interval',
|
|
473
|
+
run: async (ctx) => {
|
|
474
|
+
ctx.manager.autoUpdater.shutdown();
|
|
475
|
+
const cfg = ctx.manager.config;
|
|
476
|
+
const orig = cfg.autoUpdate;
|
|
477
|
+
cfg.autoUpdate = { enabled: false };
|
|
478
|
+
try {
|
|
479
|
+
await ctx.manager.autoUpdater.initialize(ctx.manager);
|
|
480
|
+
ctx.expect(ctx.manager.autoUpdater._feedCheckIntervalId).toBe(null);
|
|
481
|
+
ctx.expect(ctx.manager.autoUpdater._idleEvalIntervalId).toBe(null);
|
|
482
|
+
ctx.expect(ctx.manager.autoUpdater._library).toBe(null);
|
|
483
|
+
} finally {
|
|
484
|
+
cfg.autoUpdate = orig;
|
|
485
|
+
ctx.manager.autoUpdater.shutdown();
|
|
486
|
+
await ctx.manager.autoUpdater.initialize(ctx.manager);
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
},
|
|
490
|
+
|
|
491
|
+
// ─── Idle-aware install (1.2.33 + 1.2.38 refactor) ───────────────────────────
|
|
492
|
+
//
|
|
493
|
+
// The post-download install path is now centralized in the periodic tick. Tests
|
|
494
|
+
// exercise _evaluateIdleInstall() directly with stubbed installNow + _promptToInstall
|
|
495
|
+
// so we don't need a real BrowserWindow/dialog and don't actually quit the test.
|
|
496
|
+
|
|
497
|
+
{
|
|
498
|
+
name: 'markActive() bumps _lastActivityAt to current time',
|
|
499
|
+
run: async (ctx) => {
|
|
500
|
+
const u = ctx.manager.autoUpdater;
|
|
501
|
+
u._lastActivityAt = 0;
|
|
502
|
+
const before = Date.now();
|
|
503
|
+
u.markActive();
|
|
504
|
+
const after = Date.now();
|
|
505
|
+
ctx.expect(u._lastActivityAt >= before).toBe(true);
|
|
506
|
+
ctx.expect(u._lastActivityAt <= after).toBe(true);
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
name: '_onActivityIpc routes through to markActive',
|
|
511
|
+
run: async (ctx) => {
|
|
512
|
+
const u = ctx.manager.autoUpdater;
|
|
513
|
+
u._lastActivityAt = 0;
|
|
514
|
+
u._onActivityIpc();
|
|
515
|
+
ctx.expect(u._lastActivityAt > 0).toBe(true);
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
name: 'IPC channel desktop:auto-updater:activity is registered + bumps activity timestamp',
|
|
520
|
+
run: async (ctx) => {
|
|
521
|
+
const u = ctx.manager.autoUpdater;
|
|
522
|
+
// initialize() registered the listener — verify presence + simulate an inbound
|
|
523
|
+
// renderer message by invoking each registered listener directly. In-process
|
|
524
|
+
// this matches what `ipcMain.on(channel, ...)` does on a real `ipcRenderer.send`.
|
|
525
|
+
const listeners = ctx.manager.ipc._listeners['desktop:auto-updater:activity'];
|
|
526
|
+
ctx.expect(listeners).toBeDefined();
|
|
527
|
+
ctx.expect(listeners.size > 0).toBe(true);
|
|
528
|
+
u._lastActivityAt = 0;
|
|
529
|
+
listeners.forEach((fn) => fn());
|
|
530
|
+
ctx.expect(u._lastActivityAt > 0).toBe(true);
|
|
531
|
+
},
|
|
532
|
+
},
|
|
533
|
+
{
|
|
534
|
+
name: 'idle-install: when idle ≥ threshold + state=downloaded, installNow fires',
|
|
535
|
+
run: async (ctx) => {
|
|
536
|
+
const u = ctx.manager.autoUpdater;
|
|
537
|
+
const origInstall = u.installNow;
|
|
538
|
+
let installCalled = false;
|
|
539
|
+
u.installNow = async () => { installCalled = true; return true; };
|
|
540
|
+
try {
|
|
541
|
+
// Force state into "downloaded", non-user-initiated, with idle past threshold.
|
|
542
|
+
u._state = { ...u._state, code: 'downloaded', version: '1.2.3' };
|
|
543
|
+
u._userInitiated = false;
|
|
544
|
+
u._promptedForVersion = null;
|
|
545
|
+
// Simulate 16 minutes of idle.
|
|
546
|
+
u._lastActivityAt = Date.now() - (16 * 60 * 1000);
|
|
547
|
+
// Bypass dev-mode bail so we exercise the real path.
|
|
548
|
+
const origIsSimulating = u._isSimulating;
|
|
549
|
+
u._isSimulating = () => false;
|
|
550
|
+
try {
|
|
551
|
+
u._evaluateIdleInstall();
|
|
552
|
+
} finally { u._isSimulating = origIsSimulating; }
|
|
553
|
+
ctx.expect(installCalled).toBe(true);
|
|
554
|
+
} finally { u.installNow = origInstall; }
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
name: 'idle-install: when active, prompt fires once per version (no install)',
|
|
559
|
+
run: async (ctx) => {
|
|
560
|
+
const u = ctx.manager.autoUpdater;
|
|
561
|
+
const origInstall = u.installNow;
|
|
562
|
+
const origPrompt = u._promptToInstall;
|
|
563
|
+
let installCalled = false;
|
|
564
|
+
let promptCalls = 0;
|
|
565
|
+
u.installNow = async () => { installCalled = true; return true; };
|
|
566
|
+
u._promptToInstall = async (v) => { promptCalls++; };
|
|
567
|
+
try {
|
|
568
|
+
u._state = { ...u._state, code: 'downloaded', version: '1.2.3' };
|
|
569
|
+
u._userInitiated = false;
|
|
570
|
+
u._promptedForVersion = null;
|
|
571
|
+
u._lastActivityAt = Date.now(); // active right now
|
|
572
|
+
const origIsSimulating = u._isSimulating;
|
|
573
|
+
u._isSimulating = () => false;
|
|
574
|
+
try {
|
|
575
|
+
u._evaluateIdleInstall();
|
|
576
|
+
u._evaluateIdleInstall();
|
|
577
|
+
u._evaluateIdleInstall();
|
|
578
|
+
} finally { u._isSimulating = origIsSimulating; }
|
|
579
|
+
ctx.expect(installCalled).toBe(false); // user is active — no surprise install
|
|
580
|
+
ctx.expect(promptCalls).toBe(1); // dedup'd by _promptedForVersion
|
|
581
|
+
ctx.expect(u._promptedForVersion).toBe('1.2.3');
|
|
582
|
+
} finally {
|
|
583
|
+
u.installNow = origInstall;
|
|
584
|
+
u._promptToInstall = origPrompt;
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
name: 'idle-install: a NEW downloaded version re-arms the prompt',
|
|
590
|
+
run: async (ctx) => {
|
|
591
|
+
const u = ctx.manager.autoUpdater;
|
|
592
|
+
const origPrompt = u._promptToInstall;
|
|
593
|
+
let promptCalls = 0;
|
|
594
|
+
u._promptToInstall = async (v) => { promptCalls++; };
|
|
595
|
+
try {
|
|
596
|
+
u._userInitiated = false;
|
|
597
|
+
u._promptedForVersion = null;
|
|
598
|
+
u._lastActivityAt = Date.now();
|
|
599
|
+
const origIsSimulating = u._isSimulating;
|
|
600
|
+
u._isSimulating = () => false;
|
|
601
|
+
try {
|
|
602
|
+
u._state = { ...u._state, code: 'downloaded', version: '1.2.3' };
|
|
603
|
+
u._evaluateIdleInstall();
|
|
604
|
+
u._evaluateIdleInstall();
|
|
605
|
+
ctx.expect(promptCalls).toBe(1);
|
|
606
|
+
|
|
607
|
+
// Newer download — version flips. _promptedForVersion still 1.2.3, so 1.2.4 should re-prompt.
|
|
608
|
+
u._state = { ...u._state, code: 'downloaded', version: '1.2.4' };
|
|
609
|
+
u._evaluateIdleInstall();
|
|
610
|
+
ctx.expect(promptCalls).toBe(2);
|
|
611
|
+
ctx.expect(u._promptedForVersion).toBe('1.2.4');
|
|
612
|
+
} finally { u._isSimulating = origIsSimulating; }
|
|
613
|
+
} finally { u._promptToInstall = origPrompt; }
|
|
614
|
+
},
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
name: 'idle-install: skipped entirely when state !== downloaded',
|
|
618
|
+
run: async (ctx) => {
|
|
619
|
+
const u = ctx.manager.autoUpdater;
|
|
620
|
+
const origInstall = u.installNow;
|
|
621
|
+
const origPrompt = u._promptToInstall;
|
|
622
|
+
let installCalled = false;
|
|
623
|
+
let promptCalls = 0;
|
|
624
|
+
u.installNow = async () => { installCalled = true; return true; };
|
|
625
|
+
u._promptToInstall = async () => { promptCalls++; };
|
|
626
|
+
try {
|
|
627
|
+
for (const code of ['idle', 'checking', 'available', 'downloading', 'not-available', 'error']) {
|
|
628
|
+
u._state = { ...u._state, code, version: '1.2.3' };
|
|
629
|
+
u._userInitiated = false;
|
|
630
|
+
u._lastActivityAt = Date.now() - (16 * 60 * 1000); // idle, but state isn't downloaded
|
|
631
|
+
const origIsSimulating = u._isSimulating;
|
|
632
|
+
u._isSimulating = () => false;
|
|
633
|
+
try { u._evaluateIdleInstall(); } finally { u._isSimulating = origIsSimulating; }
|
|
634
|
+
}
|
|
635
|
+
ctx.expect(installCalled).toBe(false);
|
|
636
|
+
ctx.expect(promptCalls).toBe(0);
|
|
637
|
+
} finally {
|
|
638
|
+
u.installNow = origInstall;
|
|
639
|
+
u._promptToInstall = origPrompt;
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
name: 'idle-install: skipped when _userInitiated=true (consumer UI owns the path)',
|
|
645
|
+
run: async (ctx) => {
|
|
646
|
+
const u = ctx.manager.autoUpdater;
|
|
647
|
+
const origInstall = u.installNow;
|
|
648
|
+
const origPrompt = u._promptToInstall;
|
|
649
|
+
let installCalled = false;
|
|
650
|
+
let promptCalls = 0;
|
|
651
|
+
u.installNow = async () => { installCalled = true; return true; };
|
|
652
|
+
u._promptToInstall = async () => { promptCalls++; };
|
|
653
|
+
try {
|
|
654
|
+
u._state = { ...u._state, code: 'downloaded', version: '1.2.3' };
|
|
655
|
+
u._userInitiated = true;
|
|
656
|
+
u._promptedForVersion = null;
|
|
657
|
+
u._lastActivityAt = Date.now() - (16 * 60 * 1000); // idle
|
|
658
|
+
const origIsSimulating = u._isSimulating;
|
|
659
|
+
u._isSimulating = () => false;
|
|
660
|
+
try { u._evaluateIdleInstall(); } finally { u._isSimulating = origIsSimulating; }
|
|
661
|
+
ctx.expect(installCalled).toBe(false);
|
|
662
|
+
ctx.expect(promptCalls).toBe(0);
|
|
663
|
+
} finally {
|
|
664
|
+
u.installNow = origInstall;
|
|
665
|
+
u._promptToInstall = origPrompt;
|
|
666
|
+
}
|
|
667
|
+
},
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
name: 'idle-install: dev mode bails out (no install, no prompt)',
|
|
671
|
+
run: async (ctx) => {
|
|
672
|
+
const u = ctx.manager.autoUpdater;
|
|
673
|
+
const origInstall = u.installNow;
|
|
674
|
+
const origPrompt = u._promptToInstall;
|
|
675
|
+
let installCalled = false;
|
|
676
|
+
let promptCalls = 0;
|
|
677
|
+
u.installNow = async () => { installCalled = true; return true; };
|
|
678
|
+
u._promptToInstall = async () => { promptCalls++; };
|
|
679
|
+
try {
|
|
680
|
+
u._state = { ...u._state, code: 'downloaded', version: '1.2.3' };
|
|
681
|
+
u._userInitiated = false;
|
|
682
|
+
u._lastActivityAt = Date.now() - (16 * 60 * 1000);
|
|
683
|
+
const origIsSimulating = u._isSimulating;
|
|
684
|
+
u._isSimulating = () => true; // simulate OMEGA_DEV_UPDATE set
|
|
685
|
+
try { u._evaluateIdleInstall(); } finally { u._isSimulating = origIsSimulating; }
|
|
686
|
+
ctx.expect(installCalled).toBe(false);
|
|
687
|
+
ctx.expect(promptCalls).toBe(0);
|
|
688
|
+
} finally {
|
|
689
|
+
u.installNow = origInstall;
|
|
690
|
+
u._promptToInstall = origPrompt;
|
|
691
|
+
}
|
|
692
|
+
},
|
|
693
|
+
},
|
|
694
|
+
|
|
695
|
+
// ─── checkNow() concurrency / dedup ──────────────────────────────────────────
|
|
696
|
+
|
|
697
|
+
{
|
|
698
|
+
name: 'checkNow: dedupes — second call while state=checking returns without re-checking',
|
|
699
|
+
run: async (ctx) => {
|
|
700
|
+
const u = ctx.manager.autoUpdater;
|
|
701
|
+
// Stub the underlying library so we can count calls.
|
|
702
|
+
const origLib = u._library;
|
|
703
|
+
let checkCalls = 0;
|
|
704
|
+
u._library = {
|
|
705
|
+
checkForUpdates: async () => { checkCalls++; },
|
|
706
|
+
quitAndInstall: () => {},
|
|
707
|
+
autoDownload: true,
|
|
708
|
+
on: () => {},
|
|
709
|
+
};
|
|
710
|
+
try {
|
|
711
|
+
// Force state to "checking" — _readyToCheck() should refuse new checks.
|
|
712
|
+
u._state = { ...u._state, code: 'checking' };
|
|
713
|
+
await u.checkNow({ userInitiated: false });
|
|
714
|
+
await u.checkNow({ userInitiated: false });
|
|
715
|
+
await u.checkNow({ userInitiated: false });
|
|
716
|
+
ctx.expect(checkCalls).toBe(0);
|
|
717
|
+
} finally { u._library = origLib; }
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
name: 'checkNow: dedupes from each non-ready state (downloading, available, downloaded)',
|
|
722
|
+
run: async (ctx) => {
|
|
723
|
+
const u = ctx.manager.autoUpdater;
|
|
724
|
+
const origLib = u._library;
|
|
725
|
+
let checkCalls = 0;
|
|
726
|
+
u._library = {
|
|
727
|
+
checkForUpdates: async () => { checkCalls++; },
|
|
728
|
+
quitAndInstall: () => {},
|
|
729
|
+
autoDownload: true,
|
|
730
|
+
on: () => {},
|
|
731
|
+
};
|
|
732
|
+
try {
|
|
733
|
+
for (const code of ['available', 'downloading', 'downloaded']) {
|
|
734
|
+
u._state = { ...u._state, code };
|
|
735
|
+
await u.checkNow({ userInitiated: false });
|
|
736
|
+
}
|
|
737
|
+
ctx.expect(checkCalls).toBe(0);
|
|
738
|
+
} finally { u._library = origLib; }
|
|
739
|
+
},
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
name: 'checkNow: _userInitiated does NOT leak when state is non-ready (1.2.39 fix)',
|
|
743
|
+
run: async (ctx) => {
|
|
744
|
+
// Repro: background periodic check sets _userInitiated=false and starts downloading.
|
|
745
|
+
// User clicks "Check for Updates" — IPC invokes checkNow({userInitiated: true}).
|
|
746
|
+
// Pre-fix: that flipped _userInitiated to true even though the second check was
|
|
747
|
+
// skipped, breaking idle-install when the download completes.
|
|
748
|
+
const u = ctx.manager.autoUpdater;
|
|
749
|
+
u._state = { ...u._state, code: 'downloading' }; // mid-flight
|
|
750
|
+
u._userInitiated = false; // initial periodic check
|
|
751
|
+
|
|
752
|
+
await u.checkNow({ userInitiated: true }); // user clicks — should be ignored
|
|
753
|
+
|
|
754
|
+
ctx.expect(u._userInitiated).toBe(false); // still false — leak prevented
|
|
755
|
+
},
|
|
756
|
+
},
|
|
757
|
+
{
|
|
758
|
+
name: 'checkNow: _userInitiated DOES flip when the call actually performs a check',
|
|
759
|
+
run: async (ctx) => {
|
|
760
|
+
const u = ctx.manager.autoUpdater;
|
|
761
|
+
const origLib = u._library;
|
|
762
|
+
u._library = {
|
|
763
|
+
checkForUpdates: async () => {}, // no-op so the test runs synchronously
|
|
764
|
+
quitAndInstall: () => {},
|
|
765
|
+
autoDownload: true,
|
|
766
|
+
on: () => {},
|
|
767
|
+
};
|
|
768
|
+
try {
|
|
769
|
+
u._state = { ...u._state, code: 'idle' };
|
|
770
|
+
u._userInitiated = false;
|
|
771
|
+
await u.checkNow({ userInitiated: true });
|
|
772
|
+
ctx.expect(u._userInitiated).toBe(true);
|
|
773
|
+
} finally { u._library = origLib; }
|
|
774
|
+
},
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
name: 'checkNow: lastCheckedAt updates even when call is deduped',
|
|
778
|
+
run: async (ctx) => {
|
|
779
|
+
// Behavior contract: the timestamp tracks "we considered checking at this moment,"
|
|
780
|
+
// not "we successfully checked." A user click that hits the dedup guard still updates
|
|
781
|
+
// the timestamp so UI surfaces show fresh "checked just now" feedback.
|
|
782
|
+
const u = ctx.manager.autoUpdater;
|
|
783
|
+
u._state = { ...u._state, code: 'checking', lastCheckedAt: 0 };
|
|
784
|
+
const before = Date.now();
|
|
785
|
+
await u.checkNow({ userInitiated: true });
|
|
786
|
+
const after = Date.now();
|
|
787
|
+
ctx.expect(u._state.lastCheckedAt >= before).toBe(true);
|
|
788
|
+
ctx.expect(u._state.lastCheckedAt <= after).toBe(true);
|
|
789
|
+
},
|
|
790
|
+
},
|
|
791
|
+
|
|
792
|
+
// ─── Two-timer separation ────────────────────────────────────────────────────
|
|
793
|
+
//
|
|
794
|
+
// Feed-check (HTTP, expensive, hourly) and idle-eval (in-process, cheap, every
|
|
795
|
+
// minute) MUST stay separate timers — running the feed-check at idle-eval cadence
|
|
796
|
+
// hammers the GitHub release feed at 60× the necessary rate.
|
|
797
|
+
|
|
798
|
+
{
|
|
799
|
+
name: 'feed-check tick: hits the library + runs 30-day gate (no idle eval)',
|
|
800
|
+
run: async (ctx) => {
|
|
801
|
+
const u = ctx.manager.autoUpdater;
|
|
802
|
+
const origLib = u._library;
|
|
803
|
+
const origGate = u._enforceMaxAgeGate;
|
|
804
|
+
const origIdle = u._evaluateIdleInstall;
|
|
805
|
+
const calls = [];
|
|
806
|
+
u._library = {
|
|
807
|
+
checkForUpdates: async () => { calls.push('check'); },
|
|
808
|
+
quitAndInstall: () => {},
|
|
809
|
+
autoDownload: true,
|
|
810
|
+
on: () => {},
|
|
811
|
+
};
|
|
812
|
+
u._enforceMaxAgeGate = () => { calls.push('gate'); return false; };
|
|
813
|
+
u._evaluateIdleInstall = () => { calls.push('idle'); };
|
|
814
|
+
try {
|
|
815
|
+
u._state = { ...u._state, code: 'idle' };
|
|
816
|
+
await u._feedCheckTick();
|
|
817
|
+
ctx.expect(calls).toEqual(['check', 'gate']);
|
|
818
|
+
} finally {
|
|
819
|
+
u._library = origLib;
|
|
820
|
+
u._enforceMaxAgeGate = origGate;
|
|
821
|
+
u._evaluateIdleInstall = origIdle;
|
|
822
|
+
}
|
|
823
|
+
},
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
name: 'idle-eval tick: only runs idle eval (no HTTP, no gate)',
|
|
827
|
+
run: async (ctx) => {
|
|
828
|
+
const u = ctx.manager.autoUpdater;
|
|
829
|
+
const origLib = u._library;
|
|
830
|
+
const origGate = u._enforceMaxAgeGate;
|
|
831
|
+
const origIdle = u._evaluateIdleInstall;
|
|
832
|
+
const calls = [];
|
|
833
|
+
u._library = {
|
|
834
|
+
checkForUpdates: async () => { calls.push('check'); },
|
|
835
|
+
quitAndInstall: () => {},
|
|
836
|
+
autoDownload: true,
|
|
837
|
+
on: () => {},
|
|
838
|
+
};
|
|
839
|
+
u._enforceMaxAgeGate = () => { calls.push('gate'); return false; };
|
|
840
|
+
u._evaluateIdleInstall = () => { calls.push('idle'); };
|
|
841
|
+
try {
|
|
842
|
+
u._idleEvalTick();
|
|
843
|
+
ctx.expect(calls).toEqual(['idle']);
|
|
844
|
+
} finally {
|
|
845
|
+
u._library = origLib;
|
|
846
|
+
u._enforceMaxAgeGate = origGate;
|
|
847
|
+
u._evaluateIdleInstall = origIdle;
|
|
848
|
+
}
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
{
|
|
852
|
+
name: 'feed-check default cadence is 1h (production), not 1m',
|
|
853
|
+
run: (ctx) => {
|
|
854
|
+
const u = ctx.manager.autoUpdater;
|
|
855
|
+
// Sanity-guard: if someone re-merges the timers or drops the cadence to
|
|
856
|
+
// 60s again, this test fails. Production builds must not hammer the feed.
|
|
857
|
+
ctx.expect(u._options.feedCheckIntervalMs).toBe(60 * 60 * 1000);
|
|
858
|
+
ctx.expect(u._options.idleEvalIntervalMs).toBe(1 * 60 * 1000);
|
|
859
|
+
},
|
|
860
|
+
},
|
|
861
|
+
{
|
|
862
|
+
name: 'full update sequence: download → idle wait → auto-install fires',
|
|
863
|
+
run: async (ctx) => {
|
|
864
|
+
// The whole point of the new system: drive a real update through the dev simulator,
|
|
865
|
+
// then exercise the post-download decision path with stubbed installNow.
|
|
866
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
|
|
867
|
+
const u = ctx.manager.autoUpdater;
|
|
868
|
+
const origInstall = u.installNow;
|
|
869
|
+
const origIsSimulating = u._isSimulating;
|
|
870
|
+
let installCalled = false;
|
|
871
|
+
u.installNow = async () => { installCalled = true; return true; };
|
|
872
|
+
try {
|
|
873
|
+
await u.checkNow({ userInitiated: false });
|
|
874
|
+
await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
|
|
875
|
+
|
|
876
|
+
// Now at "downloaded". Force user-idle past threshold + take dev-mode out of the picture.
|
|
877
|
+
u._lastActivityAt = Date.now() - (16 * 60 * 1000);
|
|
878
|
+
u._isSimulating = () => false;
|
|
879
|
+
// Run the same evaluation the periodic tick would.
|
|
880
|
+
u._evaluateIdleInstall();
|
|
881
|
+
ctx.expect(installCalled).toBe(true);
|
|
882
|
+
} finally {
|
|
883
|
+
u.installNow = origInstall;
|
|
884
|
+
u._isSimulating = origIsSimulating;
|
|
885
|
+
await restore();
|
|
886
|
+
}
|
|
887
|
+
},
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
name: 'full update sequence: download → user-active → prompt fires (no install)',
|
|
891
|
+
run: async (ctx) => {
|
|
892
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
|
|
893
|
+
const u = ctx.manager.autoUpdater;
|
|
894
|
+
const origInstall = u.installNow;
|
|
895
|
+
const origPrompt = u._promptToInstall;
|
|
896
|
+
const origIsSimulating = u._isSimulating;
|
|
897
|
+
let installCalled = false;
|
|
898
|
+
let promptedVersion = null;
|
|
899
|
+
u.installNow = async () => { installCalled = true; return true; };
|
|
900
|
+
u._promptToInstall = async (v) => { promptedVersion = v; };
|
|
901
|
+
try {
|
|
902
|
+
await u.checkNow({ userInitiated: false });
|
|
903
|
+
await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
|
|
904
|
+
|
|
905
|
+
u._lastActivityAt = Date.now(); // active
|
|
906
|
+
u._isSimulating = () => false;
|
|
907
|
+
u._evaluateIdleInstall();
|
|
908
|
+
|
|
909
|
+
ctx.expect(installCalled).toBe(false);
|
|
910
|
+
ctx.expect(promptedVersion).toBe('999.0.0');
|
|
911
|
+
ctx.expect(u._promptedForVersion).toBe('999.0.0');
|
|
912
|
+
} finally {
|
|
913
|
+
u.installNow = origInstall;
|
|
914
|
+
u._promptToInstall = origPrompt;
|
|
915
|
+
u._isSimulating = origIsSimulating;
|
|
916
|
+
await restore();
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
},
|
|
920
|
+
|
|
921
|
+
// ─── Real-time integration test (drives the actual periodic tick) ───────────
|
|
922
|
+
//
|
|
923
|
+
// Where the unit tests above fake the clock + poke `_evaluateIdleInstall` directly,
|
|
924
|
+
// this one drives a REAL update through the dev simulator AND lets the actual
|
|
925
|
+
// periodic tick fire on its own. With `manager.isTesting() === true`, the tick
|
|
926
|
+
// cadence drops to IDLE_TICK_MS_TESTING (500ms) and the idle threshold drops to
|
|
927
|
+
// IDLE_INSTALL_THRESHOLD_MS_TESTING (3s) — so the full sequence completes inside
|
|
928
|
+
// ~5s instead of ~15min.
|
|
929
|
+
//
|
|
930
|
+
// We stub `installNow` so the test process doesn't actually quit, and lift the
|
|
931
|
+
// dev-mode bail in `_evaluateIdleInstall` so the simulated download exercises the
|
|
932
|
+
// real install decision path.
|
|
933
|
+
|
|
934
|
+
{
|
|
935
|
+
name: 'real-time end-to-end: download fires, threshold elapses, install triggers via periodic tick',
|
|
936
|
+
run: async (ctx) => {
|
|
937
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
|
|
938
|
+
const u = ctx.manager.autoUpdater;
|
|
939
|
+
const origInstall = u.installNow;
|
|
940
|
+
const origIsSimulating = u._isSimulating;
|
|
941
|
+
let installCalled = false;
|
|
942
|
+
u.installNow = async () => { installCalled = true; return true; };
|
|
943
|
+
u._isSimulating = () => false; // pretend we're in production so the eval runs
|
|
944
|
+
try {
|
|
945
|
+
// Drive the dev simulator to "downloaded".
|
|
946
|
+
await u.checkNow({ userInitiated: false });
|
|
947
|
+
await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
|
|
948
|
+
|
|
949
|
+
// Mark idle — _lastActivityAt was bumped by checkNow paths, push it back
|
|
950
|
+
// past the testing threshold (3s + small margin).
|
|
951
|
+
u._lastActivityAt = Date.now() - 4000;
|
|
952
|
+
|
|
953
|
+
// Wait for the real periodic tick (500ms cadence in tests) to fire and
|
|
954
|
+
// call our stubbed installNow. Bound at 3s to keep the test fast.
|
|
955
|
+
await waitFor(() => installCalled, { timeout: 3000 });
|
|
956
|
+
ctx.expect(installCalled).toBe(true);
|
|
957
|
+
} finally {
|
|
958
|
+
u.installNow = origInstall;
|
|
959
|
+
u._isSimulating = origIsSimulating;
|
|
960
|
+
await restore();
|
|
961
|
+
}
|
|
962
|
+
},
|
|
963
|
+
},
|
|
964
|
+
{
|
|
965
|
+
name: 'real-time end-to-end: download fires while user-active → prompt fires, install does NOT',
|
|
966
|
+
run: async (ctx) => {
|
|
967
|
+
const restore = await reinit(ctx, { OMEGA_DEV_UPDATE: 'available' });
|
|
968
|
+
const u = ctx.manager.autoUpdater;
|
|
969
|
+
const origInstall = u.installNow;
|
|
970
|
+
const origPrompt = u._promptToInstall;
|
|
971
|
+
const origIsSimulating = u._isSimulating;
|
|
972
|
+
let installCalled = false;
|
|
973
|
+
let promptedVersion = null;
|
|
974
|
+
u.installNow = async () => { installCalled = true; return true; };
|
|
975
|
+
u._promptToInstall = async (v) => { promptedVersion = v; };
|
|
976
|
+
u._isSimulating = () => false;
|
|
977
|
+
// Keep "the user" active throughout — the dev simulator takes ~2s to download
|
|
978
|
+
// (4 × 400ms ticks), and we want the activity timer to stay <3s old at all times.
|
|
979
|
+
// Bump every 500ms; the 3s threshold can never elapse if we keep doing this.
|
|
980
|
+
const keepActive = setInterval(() => u.markActive(), 500);
|
|
981
|
+
try {
|
|
982
|
+
await u.checkNow({ userInitiated: false });
|
|
983
|
+
await waitFor(() => u.getStatus().code === 'downloaded', { timeout: 5000 });
|
|
984
|
+
|
|
985
|
+
// Bump once explicitly the moment download lands — guarantees fresh activity
|
|
986
|
+
// when the next periodic tick (500ms) evaluates.
|
|
987
|
+
u.markActive();
|
|
988
|
+
|
|
989
|
+
// Wait for the prompt to fire. The first periodic tick after download lands
|
|
990
|
+
// sees "active + state=downloaded" → calls _promptToInstall.
|
|
991
|
+
await waitFor(() => promptedVersion !== null, { timeout: 2500 });
|
|
992
|
+
|
|
993
|
+
ctx.expect(promptedVersion).toBe('999.0.0');
|
|
994
|
+
ctx.expect(installCalled).toBe(false);
|
|
995
|
+
ctx.expect(u._promptedForVersion).toBe('999.0.0');
|
|
996
|
+
} finally {
|
|
997
|
+
clearInterval(keepActive);
|
|
998
|
+
u.installNow = origInstall;
|
|
999
|
+
u._promptToInstall = origPrompt;
|
|
1000
|
+
u._isSimulating = origIsSimulating;
|
|
1001
|
+
await restore();
|
|
1002
|
+
}
|
|
1003
|
+
},
|
|
1004
|
+
},
|
|
1005
|
+
|
|
1006
|
+
// ─── Mode helpers (@omega.js/backend-pattern: isDevelopment / isProduction / isTesting) ────
|
|
1007
|
+
|
|
1008
|
+
{
|
|
1009
|
+
name: 'manager.isTesting() returns true under OMEGA_TEST_MODE (set by test runner)',
|
|
1010
|
+
run: (ctx) => {
|
|
1011
|
+
ctx.expect(typeof ctx.manager.isTesting).toBe('function');
|
|
1012
|
+
ctx.expect(ctx.manager.isTesting()).toBe(true);
|
|
1013
|
+
},
|
|
1014
|
+
},
|
|
1015
|
+
{
|
|
1016
|
+
name: 'manager.isDevelopment() is false during tests (testing takes precedence)',
|
|
1017
|
+
run: (ctx) => {
|
|
1018
|
+
// The test runs unpackaged, but OMEGA_TEST_MODE=true → testing wins, so this is a
|
|
1019
|
+
// TEST environment, not development. isDevelopment() is therefore false.
|
|
1020
|
+
ctx.expect(typeof ctx.manager.isDevelopment).toBe('function');
|
|
1021
|
+
ctx.expect(ctx.manager.isDevelopment()).toBe(false);
|
|
1022
|
+
ctx.expect(ctx.manager.isTesting()).toBe(true);
|
|
1023
|
+
},
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
name: 'manager environments are mutually exclusive (exactly one true)',
|
|
1027
|
+
run: (ctx) => {
|
|
1028
|
+
const flags = [ctx.manager.isDevelopment(), ctx.manager.isTesting(), ctx.manager.isProduction()];
|
|
1029
|
+
ctx.expect(flags.filter(Boolean).length).toBe(1);
|
|
1030
|
+
ctx.expect(ctx.manager.isProduction()).toBe(false);
|
|
1031
|
+
},
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
name: 'auto-updater._idleThresholdMs() returns 3s in tests, 15min in prod',
|
|
1035
|
+
run: (ctx) => {
|
|
1036
|
+
const u = ctx.manager.autoUpdater;
|
|
1037
|
+
// Test mode currently active (manager.isTesting() === true).
|
|
1038
|
+
ctx.expect(u._idleThresholdMs()).toBe(3000);
|
|
1039
|
+
|
|
1040
|
+
// Stub manager.isTesting() to simulate prod and verify the threshold flips.
|
|
1041
|
+
const origIsTesting = ctx.manager.isTesting;
|
|
1042
|
+
ctx.manager.isTesting = () => false;
|
|
1043
|
+
try {
|
|
1044
|
+
ctx.expect(u._idleThresholdMs()).toBe(15 * 60 * 1000);
|
|
1045
|
+
} finally { ctx.manager.isTesting = origIsTesting; }
|
|
1046
|
+
},
|
|
1047
|
+
},
|
|
1048
|
+
|
|
1049
|
+
// ─── Misc internals ──────────────────────────────────────────────────────────
|
|
1050
|
+
|
|
1051
|
+
{
|
|
1052
|
+
name: 'shutdown clears pending timers + interval + idle-install state',
|
|
1053
|
+
run: async (ctx) => {
|
|
1054
|
+
const restore = await reinit(ctx, {});
|
|
1055
|
+
const u = ctx.manager.autoUpdater;
|
|
1056
|
+
try {
|
|
1057
|
+
u._pendingTimers.push(setTimeout(() => {}, 1000000));
|
|
1058
|
+
u._promptedForVersion = '1.2.3';
|
|
1059
|
+
u.shutdown();
|
|
1060
|
+
ctx.expect(u._feedCheckIntervalId).toBe(null);
|
|
1061
|
+
ctx.expect(u._idleEvalIntervalId).toBe(null);
|
|
1062
|
+
ctx.expect(u._pendingTimers).toEqual([]);
|
|
1063
|
+
ctx.expect(u._initialized).toBe(false);
|
|
1064
|
+
ctx.expect(u._library).toBe(null);
|
|
1065
|
+
ctx.expect(u._userInitiated).toBe(false);
|
|
1066
|
+
ctx.expect(u._promptedForVersion).toBe(null);
|
|
1067
|
+
ctx.expect(u._activityHooksWired).toBe(false);
|
|
1068
|
+
} finally { await restore(); }
|
|
1069
|
+
},
|
|
1070
|
+
},
|
|
1071
|
+
{
|
|
1072
|
+
name: '_menuItemFieldsForState produces correct label per state',
|
|
1073
|
+
run: (ctx) => {
|
|
1074
|
+
const u = ctx.manager.autoUpdater;
|
|
1075
|
+
const cases = [
|
|
1076
|
+
['idle', 'Check for Updates...', true],
|
|
1077
|
+
['error', 'Check for Updates...', true],
|
|
1078
|
+
['not-available', "You're up to date", true],
|
|
1079
|
+
['checking', 'Checking for Updates...', false],
|
|
1080
|
+
];
|
|
1081
|
+
for (const [code, label, enabled] of cases) {
|
|
1082
|
+
u._state = { ...u._state, code, version: null, percent: 0 };
|
|
1083
|
+
const r = u._menuItemFieldsForState();
|
|
1084
|
+
ctx.expect(r.label).toBe(label);
|
|
1085
|
+
ctx.expect(r.enabled).toBe(enabled);
|
|
1086
|
+
}
|
|
1087
|
+
// version-bearing states
|
|
1088
|
+
u._state = { ...u._state, code: 'available', version: '2.0.0' };
|
|
1089
|
+
ctx.expect(u._menuItemFieldsForState().label).toBe('Downloading Update v2.0.0...');
|
|
1090
|
+
ctx.expect(u._menuItemFieldsForState().enabled).toBe(false);
|
|
1091
|
+
|
|
1092
|
+
u._state = { ...u._state, code: 'downloaded', version: '2.0.0' };
|
|
1093
|
+
ctx.expect(u._menuItemFieldsForState().label).toBe('Restart to Update v2.0.0');
|
|
1094
|
+
ctx.expect(u._menuItemFieldsForState().enabled).toBe(true);
|
|
1095
|
+
|
|
1096
|
+
u._state = { ...u._state, code: 'downloading', percent: 42 };
|
|
1097
|
+
ctx.expect(u._menuItemFieldsForState().label).toBe('Downloading Update (42%)');
|
|
1098
|
+
ctx.expect(u._menuItemFieldsForState().enabled).toBe(false);
|
|
1099
|
+
},
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
name: 'IPC: desktop:auto-updater:check-now invokes checkNow with userInitiated=true',
|
|
1103
|
+
run: async (ctx) => {
|
|
1104
|
+
const u = ctx.manager.autoUpdater;
|
|
1105
|
+
const origCheck = u.checkNow;
|
|
1106
|
+
let receivedOpts = null;
|
|
1107
|
+
u.checkNow = async (opts) => { receivedOpts = opts; return u.getStatus(); };
|
|
1108
|
+
try {
|
|
1109
|
+
await ctx.manager.ipc.invoke('desktop:auto-updater:check-now');
|
|
1110
|
+
ctx.expect(receivedOpts).toBeDefined();
|
|
1111
|
+
ctx.expect(receivedOpts.userInitiated).toBe(true);
|
|
1112
|
+
} finally { u.checkNow = origCheck; }
|
|
1113
|
+
},
|
|
1114
|
+
},
|
|
1115
|
+
{
|
|
1116
|
+
name: 'IPC: desktop:auto-updater:install-now invokes installNow',
|
|
1117
|
+
run: async (ctx) => {
|
|
1118
|
+
const u = ctx.manager.autoUpdater;
|
|
1119
|
+
const origInstall = u.installNow;
|
|
1120
|
+
let installCalled = false;
|
|
1121
|
+
u.installNow = async () => { installCalled = true; return false; };
|
|
1122
|
+
try {
|
|
1123
|
+
await ctx.manager.ipc.invoke('desktop:auto-updater:install-now');
|
|
1124
|
+
ctx.expect(installCalled).toBe(true);
|
|
1125
|
+
} finally { u.installNow = origInstall; }
|
|
1126
|
+
},
|
|
1127
|
+
},
|
|
1128
|
+
{
|
|
1129
|
+
name: 'installNow: returns false when state is not "downloaded"',
|
|
1130
|
+
run: async (ctx) => {
|
|
1131
|
+
const u = ctx.manager.autoUpdater;
|
|
1132
|
+
u._state = { ...u._state, code: 'idle' };
|
|
1133
|
+
const r = await u.installNow();
|
|
1134
|
+
ctx.expect(r).toBe(false);
|
|
1135
|
+
},
|
|
1136
|
+
},
|
|
1137
|
+
],
|
|
1138
|
+
});
|