From 9066c247238d1c20e583ec355596d9a61e8c7643 Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Thu, 3 Sep 2026 22:55:24 +0530 Subject: [PATCH] fix: prevent Cordova exec startup race --- src/lib/pluginContext.js | 5 ++++- tests/unit/pluginContext.test.js | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/unit/pluginContext.test.js diff --git a/src/lib/pluginContext.js b/src/lib/pluginContext.js index 2cf460577c..f1087a8e17 100644 --- a/src/lib/pluginContext.js +++ b/src/lib/pluginContext.js @@ -1,4 +1,7 @@ -const cordovaExec = cordova.exec.bind(cordova); +// `cordova.exec` is mapped asynchronously while Cordova starts. Importing this +// module can happen before that mapping completes, so use Cordova's internal +// module loader, which is available as soon as cordova.js has been evaluated. +const cordovaExec = cordova.require("cordova/exec"); const exec = (resolve, reject, action, args) => cordovaExec(resolve, reject, "Tee", action, args); diff --git a/tests/unit/pluginContext.test.js b/tests/unit/pluginContext.test.js new file mode 100644 index 0000000000..2523220c99 --- /dev/null +++ b/tests/unit/pluginContext.test.js @@ -0,0 +1,27 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; + +afterEach(() => { + vi.resetModules(); + vi.unstubAllGlobals(); +}); + +describe("plugin context Cordova bridge", () => { + it("loads before cordova.exec is mapped", async () => { + const nativeExec = vi.fn((resolve, _reject, _service, action) => { + if (action === "establishConnection") { + resolve("trusted-session"); + } else if (action === "requestToken") { + resolve("plugin-token"); + } + }); + const requireModule = vi.fn(() => nativeExec); + vi.stubGlobal("cordova", { require: requireModule }); + + const pluginContext = await import("lib/pluginContext"); + + expect(requireModule).toHaveBeenCalledWith("cordova/exec"); + expect(cordova.exec).toBeUndefined(); + await expect(pluginContext.connect()).resolves.toBe(true); + await expect(pluginContext.default("example.plugin", "{}")).resolves.toBeTruthy(); + }); +});