From 880f1944be52593df22f61abb9ace2579055d302 Mon Sep 17 00:00:00 2001 From: Michele Esposito <34438276+mikesposito@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:37:08 +0200 Subject: [PATCH] cherry-pick `1ef5ab9` --- jest.config.js | 8 +++--- src/ledger-iframe-bridge.test.ts | 30 ++++++-------------- src/ledger-iframe-bridge.ts | 48 +++++++++----------------------- test/document.shim.ts | 41 +++++++++++---------------- 4 files changed, 42 insertions(+), 85 deletions(-) diff --git a/jest.config.js b/jest.config.js index 95af46b4..35fc328c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -41,10 +41,10 @@ module.exports = { // An object that configures minimum threshold enforcement for coverage results coverageThreshold: { global: { - branches: 69.76, - functions: 88.73, - lines: 81.93, - statements: 81.84, + branches: 71.09, + functions: 88.88, + lines: 83.86, + statements: 83.75, }, }, diff --git a/src/ledger-iframe-bridge.test.ts b/src/ledger-iframe-bridge.test.ts index 9c12b628..877da92f 100644 --- a/src/ledger-iframe-bridge.test.ts +++ b/src/ledger-iframe-bridge.test.ts @@ -36,20 +36,6 @@ function isIFrameValid( ); } -/** - * Simulates the loading of an iframe by calling the onload function. - * - * @param iframe - The iframe to simulate the loading of. - * @returns Returns a promise that resolves when the onload function is called. - */ -async function simulateIFrameLoad(iframe?: HTMLIFrameElementShim) { - if (!isIFrameValid(iframe)) { - throw new Error('the iframe is not valid'); - } - // we call manually the onload event to simulate the iframe loading - return await iframe.onload(); -} - const LEDGER_IFRAME_ID = 'LEDGER-IFRAME'; const BRIDGE_URL = 'https://metamask.github.io/eth-ledger-bridge-keyring'; const INVALID_URL_ERROR = 'bridgeURL is not a valid URL'; @@ -80,7 +66,6 @@ describe('LedgerIframeBridge', function () { bridgeUrl: BRIDGE_URL, }); await bridge.init(); - await simulateIFrameLoad(bridge.iframe); }); afterEach(function () { @@ -123,15 +108,11 @@ describe('LedgerIframeBridge', function () { describe('init', function () { it('sets up the listener and iframe', async function () { bridge = new LedgerIframeBridge(); - const addEventListenerSpy = jest.spyOn(global.window, 'addEventListener'); await bridge.init(); expect(addEventListenerSpy).toHaveBeenCalledTimes(1); - expect(bridge.iframeLoaded).toBe(false); - - await simulateIFrameLoad(bridge.iframe); expect(bridge.iframeLoaded).toBe(true); }); }); @@ -200,8 +181,6 @@ describe('LedgerIframeBridge', function () { describe('updateTransportMethod', function () { it('sends and processes a successful ledger-update-transport message', async function () { - bridge.iframeLoaded = true; - const transportType = 'u2f'; stubKeyringIFramePostMessage(bridge, (message) => { @@ -227,6 +206,14 @@ describe('LedgerIframeBridge', function () { expect(bridge.iframe?.contentWindow?.postMessage).toHaveBeenCalled(); }); + it('throws an error when the bridge is not initialized', async function () { + bridge = new LedgerIframeBridge(); + + await expect(bridge.updateTransportMethod('u2f')).rejects.toThrow( + 'The iframe is not loaded yet', + ); + }); + it('throws an error when a ledger-update-transport message is not successful', async function () { bridge.iframeLoaded = true; @@ -527,7 +514,6 @@ describe('LedgerIframeBridge', function () { addEventListenerSpy = jest.spyOn(global.window, 'addEventListener'); bridge = new LedgerIframeBridge(); await bridge.init(); - await simulateIFrameLoad(bridge.iframe); }); describe('when configurate bridge url', function () { diff --git a/src/ledger-iframe-bridge.ts b/src/ledger-iframe-bridge.ts index f87aca5c..a73987e9 100644 --- a/src/ledger-iframe-bridge.ts +++ b/src/ledger-iframe-bridge.ts @@ -110,12 +110,6 @@ export class LedgerIframeBridge messageCallbacks: Record void> = {}; - delayedPromise?: { - resolve: (value: boolean) => void; - reject: (error: unknown) => void; - transportType: string; - }; - constructor( opts: LedgerIframeBridgeOptions = { bridgeUrl: 'https://metamask.github.io/eth-ledger-bridge-keyring', @@ -128,7 +122,7 @@ export class LedgerIframeBridge } async init() { - this.#setupIframe(this.#opts.bridgeUrl); + await this.#setupIframe(this.#opts.bridgeUrl); this.eventListener = this.#eventListener.bind(this, this.#opts.bridgeUrl); @@ -178,12 +172,7 @@ export class LedgerIframeBridge // If the iframe isn't loaded yet, let's store the desired transportType value and // optimistically return a successful promise if (!this.iframeLoaded) { - this.delayedPromise = { - resolve, - reject, - transportType, - }; - return; + throw new Error('The iframe is not loaded yet'); } this.#sendMessage( @@ -282,28 +271,17 @@ export class LedgerIframeBridge }); } - #setupIframe(bridgeUrl: string) { - this.iframe = document.createElement('iframe'); - this.iframe.src = bridgeUrl; - this.iframe.allow = `hid 'src'`; - this.iframe.onload = async () => { - // If the ledger live preference was set before the iframe is loaded, - // set it after the iframe has loaded - this.iframeLoaded = true; - if (this.delayedPromise) { - try { - const result = await this.updateTransportMethod( - this.delayedPromise.transportType, - ); - this.delayedPromise.resolve(result); - } catch (error) { - this.delayedPromise.reject(error); - } finally { - delete this.delayedPromise; - } - } - }; - document.head.appendChild(this.iframe); + async #setupIframe(bridgeUrl: string): Promise { + return new Promise((resolve) => { + this.iframe = document.createElement('iframe'); + this.iframe.src = bridgeUrl; + this.iframe.allow = `hid 'src'`; + this.iframe.onload = async () => { + this.iframeLoaded = true; + resolve(); + }; + document.head.appendChild(this.iframe); + }); } #getOrigin(bridgeUrl: string) { diff --git a/test/document.shim.ts b/test/document.shim.ts index ceba08c3..42c4983a 100644 --- a/test/document.shim.ts +++ b/test/document.shim.ts @@ -1,32 +1,25 @@ // eslint-disable-next-line import/no-mutable-exports let documentShim: any; -try { - documentShim = document || { - head: { - appendChild: () => false, +const shim = { + head: { + appendChild: (child: { onload?: () => void }) => { + child.onload?.(); }, - createElement: () => ({ - src: false, - contentWindow: { - postMessage: () => false, - }, - }), - addEventListener: () => false, - }; -} catch (error) { - documentShim = { - head: { - appendChild: () => false, + }, + createElement: () => ({ + src: false, + contentWindow: { + postMessage: () => false, }, - createElement: () => ({ - src: false, - contentWindow: { - postMessage: () => false, - }, - }), - addEventListener: () => false, - }; + }), + addEventListener: () => false, +}; + +try { + documentShim = document || shim; +} catch (error) { + documentShim = shim; } export default documentShim;