From 0665a3392ec43d22b3239269319156a226ff0eb5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 8 Feb 2026 03:44:38 +0000 Subject: [PATCH] refactor(app): Move heavy initialization from constructor to setup() - Introduces a `setup()` method in `Application` class to handle complex initialization and side effects. - Updates `constructor` to be lightweight, only storing configuration options. - Updates `js/bootstrap.js`, `js/embed.js`, and relevant tests to call `app.setup()` explicitly after instantiation. - Resolves "Heavy Constructor" code health issue. - Fixes `tests/video-modal-zap.test.mjs` environment issue with `globalThis.navigator`. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- js/app.js | 11 +++++++++++ js/bootstrap.js | 4 +++- js/embed.js | 2 ++ tests/video-modal-accessibility.test.mjs | 1 + tests/video-modal-zap.test.mjs | 11 ++++++++++- 5 files changed, 27 insertions(+), 2 deletions(-) diff --git a/js/app.js b/js/app.js index 0f040880..60233630 100644 --- a/js/app.js +++ b/js/app.js @@ -241,6 +241,15 @@ class Application { constructor({ services = {}, ui = {}, helpers = {}, loadView: viewLoader } = {}) { this.loadView = typeof viewLoader === "function" ? viewLoader : null; + this._setupOptions = { services, ui, helpers }; + this._isSetup = false; + } + + setup() { + if (this._isSetup) return; + this._isSetup = true; + + const { services = {}, ui = {}, helpers = {} } = this._setupOptions || {}; const bootstrapServices = { ...services, @@ -502,6 +511,8 @@ class Application { }; onActiveSignerChanged(this.handleShareNostrSignerChange); this.updateShareNostrAuthState({ reason: "init" }); + + delete this._setupOptions; } /** diff --git a/js/bootstrap.js b/js/bootstrap.js index b8e82d8b..bcbfa1b3 100644 --- a/js/bootstrap.js +++ b/js/bootstrap.js @@ -30,10 +30,12 @@ function mergeServices(overrides = {}) { } export async function createApplication({ services, loadView: loadViewOverride } = {}) { - return new Application({ + const app = new Application({ services: mergeServices(services), loadView: typeof loadViewOverride === "function" ? loadViewOverride : loadView, }); + app.setup(); + return app; } export default createApplication; diff --git a/js/embed.js b/js/embed.js index a6f76577..fec5d152 100644 --- a/js/embed.js +++ b/js/embed.js @@ -102,6 +102,8 @@ app.showSuccess = (message) => { setStatus(message, "success"); }; +app.setup(); + const resolvePlaybackPreference = (value) => { if (typeof value !== "string") { return null; diff --git a/tests/video-modal-accessibility.test.mjs b/tests/video-modal-accessibility.test.mjs index ebfaebfd..7401f3cf 100644 --- a/tests/video-modal-accessibility.test.mjs +++ b/tests/video-modal-accessibility.test.mjs @@ -599,6 +599,7 @@ async function setupPlaybackHarness() { helpers, ui: { videoModal: () => modalStub }, }); + app.setup(); app.showError = () => {}; app.showSuccess = () => {}; diff --git a/tests/video-modal-zap.test.mjs b/tests/video-modal-zap.test.mjs index 0a1ae2bd..c529d0fd 100644 --- a/tests/video-modal-zap.test.mjs +++ b/tests/video-modal-zap.test.mjs @@ -143,7 +143,15 @@ if (typeof globalThis.WebSocket === "undefined") { } globalThis.document = documentStub; -globalThis.navigator = windowStub.navigator; +try { + Object.defineProperty(globalThis, "navigator", { + value: windowStub.navigator, + writable: true, + configurable: true, + }); +} catch (e) { + // Fallback for environments where navigator is read-only +} globalThis.location = windowStub.location; if (typeof globalThis.fetch === "undefined") { @@ -302,6 +310,7 @@ async function createApp({ splitAndZap }) { videoModal: () => modalStub, }, }); + app.setup(); app.boundVideoModalZapOpenHandler = (event = {}) => { const requiresLogin = Boolean(event?.detail?.requiresLogin);