From 2c24a03088d0210febbd7fa95adeecf8762d7dd8 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Thu, 29 Feb 2024 18:04:37 -0800 Subject: [PATCH 01/16] Adds createResponseTimeoutError to SDK error factories --- packages/@magic-sdk/provider/src/core/sdk-exceptions.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts b/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts index 0f72f57cc..20f610e25 100644 --- a/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts +++ b/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts @@ -110,6 +110,10 @@ export function createModalNotReadyError() { return new MagicSDKError(SDKErrorCode.ModalNotReady, 'Modal is not ready.'); } +export function createResponseTimeoutError(msg: string) { + return new MagicSDKError(SDKErrorCode.ResponseTimeout, msg); +} + export function createMalformedResponseError() { return new MagicSDKError(SDKErrorCode.MalformedResponse, 'Response from the Magic iframe is malformed.'); } From fc547724b13216a59c3e6d8f5af92b638d431e00 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Thu, 29 Feb 2024 18:05:01 -0800 Subject: [PATCH 02/16] Adds ResponseTimeout SDKErrorCode --- packages/@magic-sdk/types/src/core/exception-types.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@magic-sdk/types/src/core/exception-types.ts b/packages/@magic-sdk/types/src/core/exception-types.ts index 322623b55..a4d300896 100644 --- a/packages/@magic-sdk/types/src/core/exception-types.ts +++ b/packages/@magic-sdk/types/src/core/exception-types.ts @@ -5,6 +5,7 @@ export enum SDKErrorCode { InvalidArgument = 'INVALID_ARGUMENT', ExtensionNotInitialized = 'EXTENSION_NOT_INITIALIZED', IncompatibleExtensions = 'INCOMPATIBLE_EXTENSIONS', + ResponseTimeout = 'RESPONSE_TIMEOUT', } export enum SDKWarningCode { From b978bf4aeb6096ed0455f735865606a5dd1a2c38 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Thu, 29 Feb 2024 18:05:54 -0800 Subject: [PATCH 03/16] Throw ResponseTimeout SDK Error for responses that take more than 5 seconds --- .../src/react-native-webview-controller.tsx | 26 ++++++++++++++++++- .../src/react-native-webview-controller.tsx | 26 ++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx b/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx index 34e5b2b72..22f4fdfc7 100644 --- a/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx +++ b/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx @@ -2,7 +2,7 @@ import React, { useState, useCallback, useMemo, useEffect } from 'react'; import { Linking, StyleSheet, View } from 'react-native'; import { WebView } from 'react-native-webview'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { ViewController, createModalNotReadyError } from '@magic-sdk/provider'; +import { ViewController, createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider'; import { MagicMessageEvent } from '@magic-sdk/types'; import { isTypedArray } from 'lodash'; import Global = NodeJS.Global; @@ -62,6 +62,7 @@ export class ReactNativeWebViewController extends ViewController { private webView!: WebView | null; private container!: ViewWrapper | null; private styles: any; + private messageTimeouts = new Map(); protected init() { this.webView = null; @@ -139,6 +140,18 @@ export class ReactNativeWebViewController extends ViewController { }, [show]); const handleWebViewMessage = useCallback((event: any) => { + const data = JSON.parse(event.nativeEvent.data); + + if (data && data.response && data.response.id) { + const messageId = data.response.id; + + // Clear timeout if message is received in time + if (this.messageTimeouts.has(messageId)) { + clearTimeout(this.messageTimeouts.get(messageId)); + this.messageTimeouts.delete(messageId); + } + } + // Process the received message this.handleReactNativeWebViewMessage(event); }, []); @@ -216,7 +229,18 @@ export class ReactNativeWebViewController extends ViewController { } protected async _post(data: any) { + const methodType = data.payload.method; + const messageId = data.payload.id; + if (this.webView && (this.webView as any).postMessage) { + // Setup timeout for message response + const timeout = setTimeout(() => { + this.messageTimeouts.delete(messageId); + throw createResponseTimeoutError(`Response timed out for method: ${methodType} with message id: ${messageId}`); + }, 5000); // 5-second timeout + + this.messageTimeouts.set(messageId, timeout); + (this.webView as any).postMessage( JSON.stringify(data, (key, value) => { // parse Typed Array to Stringify object diff --git a/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx b/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx index 97c1f802a..b160b1970 100644 --- a/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx +++ b/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx @@ -2,7 +2,7 @@ import React, { useState, useCallback, useMemo, useEffect } from 'react'; import { Linking, StyleSheet, View } from 'react-native'; import { WebView } from 'react-native-webview'; import { SafeAreaView } from 'react-native-safe-area-context'; -import { ViewController, createModalNotReadyError } from '@magic-sdk/provider'; +import { ViewController, createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider'; import { MagicMessageEvent } from '@magic-sdk/types'; import { isTypedArray } from 'lodash'; import Global = NodeJS.Global; @@ -62,6 +62,7 @@ export class ReactNativeWebViewController extends ViewController { private webView!: WebView | null; private container!: ViewWrapper | null; private styles: any; + private messageTimeouts = new Map(); protected init() { this.webView = null; @@ -139,6 +140,18 @@ export class ReactNativeWebViewController extends ViewController { }, [show]); const handleWebViewMessage = useCallback((event: any) => { + const data = JSON.parse(event.nativeEvent.data); + + if (data && data.response && data.response.id) { + const messageId = data.response.id; + + // Clear timeout if message is received in time + if (this.messageTimeouts.has(messageId)) { + clearTimeout(this.messageTimeouts.get(messageId)); + this.messageTimeouts.delete(messageId); + } + } + // Process the received message this.handleReactNativeWebViewMessage(event); }, []); @@ -216,7 +229,18 @@ export class ReactNativeWebViewController extends ViewController { } protected async _post(data: any) { + const methodType = data.payload.method; + const messageId = data.payload.id; + if (this.webView && (this.webView as any).postMessage) { + // Setup timeout for message response + const timeout = setTimeout(() => { + this.messageTimeouts.delete(messageId); + throw createResponseTimeoutError(`Response timed out for method: ${methodType} with message id: ${messageId}`); + }, 5000); // 5-second timeout + + this.messageTimeouts.set(messageId, timeout); + (this.webView as any).postMessage( JSON.stringify(data, (key, value) => { // parse Typed Array to Stringify object From 313aa2992a933df13f7b0657670940278a800052 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Thu, 29 Feb 2024 18:09:58 -0800 Subject: [PATCH 04/16] update yarn.lock --- yarn.lock | 96 +++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/yarn.lock b/yarn.lock index eea7c786f..42e053a1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2045,7 +2045,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/algorand@workspace:packages/@magic-ext/algorand" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2054,8 +2054,8 @@ __metadata: resolution: "@magic-ext/aptos@workspace:packages/@magic-ext/aptos" dependencies: "@aptos-labs/wallet-adapter-core": ^2.2.0 - "@magic-sdk/commons": ^20.0.0 - "@magic-sdk/provider": ^24.0.0 + "@magic-sdk/commons": ^21.0.0 + "@magic-sdk/provider": ^25.0.0 aptos: ^1.8.5 peerDependencies: "@aptos-labs/wallet-adapter-core": ^2.2.0 @@ -2067,7 +2067,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/avalanche@workspace:packages/@magic-ext/avalanche" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2075,7 +2075,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/bitcoin@workspace:packages/@magic-ext/bitcoin" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2083,7 +2083,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/conflux@workspace:packages/@magic-ext/conflux" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2091,7 +2091,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/cosmos@workspace:packages/@magic-ext/cosmos" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2099,7 +2099,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/ed25519@workspace:packages/@magic-ext/ed25519" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2107,7 +2107,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/flow@workspace:packages/@magic-ext/flow" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 "@onflow/fcl": ^1.4.1 "@onflow/types": ^1.1.0 peerDependencies: @@ -2120,8 +2120,8 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/gdkms@workspace:packages/@magic-ext/gdkms" dependencies: - "@magic-sdk/commons": ^20.0.0 - "@magic-sdk/types": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 + "@magic-sdk/types": ^21.0.0 "@peculiar/webcrypto": ^1.4.3 languageName: unknown linkType: soft @@ -2130,7 +2130,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/harmony@workspace:packages/@magic-ext/harmony" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2148,7 +2148,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/icon@workspace:packages/@magic-ext/icon" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2156,7 +2156,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/near@workspace:packages/@magic-ext/near" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2164,17 +2164,17 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/oauth2@workspace:packages/@magic-ext/oauth2" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 "@types/crypto-js": ~3.1.47 crypto-js: ^3.3.0 languageName: unknown linkType: soft -"@magic-ext/oauth@^18.0.0, @magic-ext/oauth@workspace:packages/@magic-ext/oauth": +"@magic-ext/oauth@^19.0.0, @magic-ext/oauth@workspace:packages/@magic-ext/oauth": version: 0.0.0-use.local resolution: "@magic-ext/oauth@workspace:packages/@magic-ext/oauth" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 "@types/crypto-js": ~3.1.47 crypto-js: ^3.3.0 languageName: unknown @@ -2184,7 +2184,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/oidc@workspace:packages/@magic-ext/oidc" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2192,7 +2192,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/polkadot@workspace:packages/@magic-ext/polkadot" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2200,8 +2200,8 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/react-native-bare-oauth@workspace:packages/@magic-ext/react-native-bare-oauth" dependencies: - "@magic-sdk/react-native-bare": ^25.0.0 - "@magic-sdk/types": ^20.0.0 + "@magic-sdk/react-native-bare": ^26.0.0 + "@magic-sdk/types": ^21.0.0 "@types/crypto-js": ~3.1.47 crypto-js: ^3.3.0 react-native-device-info: ^10.3.0 @@ -2216,7 +2216,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/react-native-expo-oauth@workspace:packages/@magic-ext/react-native-expo-oauth" dependencies: - "@magic-sdk/react-native-expo": ^25.0.0 + "@magic-sdk/react-native-expo": ^26.0.0 "@magic-sdk/types": ^10.0.0 "@types/crypto-js": ~3.1.47 crypto-js: ^3.3.0 @@ -2231,7 +2231,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/solana@workspace:packages/@magic-ext/solana" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 "@solana/web3.js": ^1.87.2 peerDependencies: "@solana/web3.js": ^1.87.2 @@ -2242,7 +2242,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/taquito@workspace:packages/@magic-ext/taquito" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2250,7 +2250,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/terra@workspace:packages/@magic-ext/terra" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2258,7 +2258,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/tezos@workspace:packages/@magic-ext/tezos" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2266,7 +2266,7 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/webauthn@workspace:packages/@magic-ext/webauthn" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft @@ -2274,16 +2274,16 @@ __metadata: version: 0.0.0-use.local resolution: "@magic-ext/zilliqa@workspace:packages/@magic-ext/zilliqa" dependencies: - "@magic-sdk/commons": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 languageName: unknown linkType: soft -"@magic-sdk/commons@^20.0.0, @magic-sdk/commons@workspace:packages/@magic-sdk/commons": +"@magic-sdk/commons@^21.0.0, @magic-sdk/commons@workspace:packages/@magic-sdk/commons": version: 0.0.0-use.local resolution: "@magic-sdk/commons@workspace:packages/@magic-sdk/commons" dependencies: - "@magic-sdk/provider": ^24.0.0 - "@magic-sdk/types": ^20.0.0 + "@magic-sdk/provider": ^25.0.0 + "@magic-sdk/types": ^21.0.0 peerDependencies: "@magic-sdk/provider": ">=18.6.0" "@magic-sdk/types": ">=15.8.0" @@ -2307,17 +2307,17 @@ __metadata: "@babel/core": ^7.9.6 "@babel/plugin-proposal-optional-chaining": ^7.9.0 "@babel/runtime": ^7.9.6 - "@magic-ext/oauth": ^18.0.0 - magic-sdk: ^24.0.0 + "@magic-ext/oauth": ^19.0.0 + magic-sdk: ^25.0.0 languageName: unknown linkType: soft -"@magic-sdk/provider@^24.0.0, @magic-sdk/provider@workspace:packages/@magic-sdk/provider": +"@magic-sdk/provider@^25.0.0, @magic-sdk/provider@workspace:packages/@magic-sdk/provider": version: 0.0.0-use.local resolution: "@magic-sdk/provider@workspace:packages/@magic-sdk/provider" dependencies: "@babel/plugin-transform-modules-commonjs": ^7.9.6 - "@magic-sdk/types": ^20.0.0 + "@magic-sdk/types": ^21.0.0 "@peculiar/webcrypto": ^1.1.7 eventemitter3: ^4.0.4 localforage: ^1.7.4 @@ -2329,7 +2329,7 @@ __metadata: languageName: unknown linkType: soft -"@magic-sdk/react-native-bare@^25.0.0, @magic-sdk/react-native-bare@workspace:packages/@magic-sdk/react-native-bare": +"@magic-sdk/react-native-bare@^26.0.0, @magic-sdk/react-native-bare@workspace:packages/@magic-sdk/react-native-bare": version: 0.0.0-use.local resolution: "@magic-sdk/react-native-bare@workspace:packages/@magic-sdk/react-native-bare" dependencies: @@ -2337,9 +2337,9 @@ __metadata: "@babel/core": ^7.15.0 "@babel/plugin-transform-flow-strip-types": ^7.14.5 "@babel/runtime": ~7.10.4 - "@magic-sdk/commons": ^20.0.0 - "@magic-sdk/provider": ^24.0.0 - "@magic-sdk/types": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 + "@magic-sdk/provider": ^25.0.0 + "@magic-sdk/types": ^21.0.0 "@react-native-async-storage/async-storage": ^1.15.5 "@react-native-community/netinfo": ">11.0.0" "@testing-library/react-native": ^12.4.0 @@ -2369,7 +2369,7 @@ __metadata: languageName: unknown linkType: soft -"@magic-sdk/react-native-expo@^25.0.0, @magic-sdk/react-native-expo@workspace:packages/@magic-sdk/react-native-expo": +"@magic-sdk/react-native-expo@^26.0.0, @magic-sdk/react-native-expo@workspace:packages/@magic-sdk/react-native-expo": version: 0.0.0-use.local resolution: "@magic-sdk/react-native-expo@workspace:packages/@magic-sdk/react-native-expo" dependencies: @@ -2377,9 +2377,9 @@ __metadata: "@babel/core": ^7.15.0 "@babel/plugin-transform-flow-strip-types": ^7.14.5 "@babel/runtime": ~7.10.4 - "@magic-sdk/commons": ^20.0.0 - "@magic-sdk/provider": ^24.0.0 - "@magic-sdk/types": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 + "@magic-sdk/provider": ^25.0.0 + "@magic-sdk/types": ^21.0.0 "@react-native-async-storage/async-storage": ^1.15.5 "@react-native-community/netinfo": ">11.0.0" "@testing-library/react-native": ^12.4.0 @@ -2409,7 +2409,7 @@ __metadata: languageName: unknown linkType: soft -"@magic-sdk/types@^20.0.0, @magic-sdk/types@workspace:packages/@magic-sdk/types": +"@magic-sdk/types@^21.0.0, @magic-sdk/types@workspace:packages/@magic-sdk/types": version: 0.0.0-use.local resolution: "@magic-sdk/types@workspace:packages/@magic-sdk/types" languageName: unknown @@ -12754,16 +12754,16 @@ fsevents@^2.3.2: languageName: unknown linkType: soft -"magic-sdk@^24.0.0, magic-sdk@workspace:packages/magic-sdk": +"magic-sdk@^25.0.0, magic-sdk@workspace:packages/magic-sdk": version: 0.0.0-use.local resolution: "magic-sdk@workspace:packages/magic-sdk" dependencies: "@babel/core": ^7.9.6 "@babel/plugin-proposal-optional-chaining": ^7.9.0 "@babel/runtime": ^7.9.6 - "@magic-sdk/commons": ^20.0.0 - "@magic-sdk/provider": ^24.0.0 - "@magic-sdk/types": ^20.0.0 + "@magic-sdk/commons": ^21.0.0 + "@magic-sdk/provider": ^25.0.0 + "@magic-sdk/types": ^21.0.0 localforage: ^1.7.4 localforage-driver-memory: ^1.0.5 languageName: unknown From 0f6b1e31b54c733f8e9072683b910987e0f38557 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Mon, 4 Mar 2024 11:31:58 -0800 Subject: [PATCH 05/16] Pass params to createResponseTimeoutError --- packages/@magic-sdk/provider/src/core/sdk-exceptions.ts | 7 +++++-- .../src/react-native-webview-controller.tsx | 4 ++-- .../src/react-native-webview-controller.tsx | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts b/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts index 20f610e25..f5d1ee1a4 100644 --- a/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts +++ b/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts @@ -110,8 +110,11 @@ export function createModalNotReadyError() { return new MagicSDKError(SDKErrorCode.ModalNotReady, 'Modal is not ready.'); } -export function createResponseTimeoutError(msg: string) { - return new MagicSDKError(SDKErrorCode.ResponseTimeout, msg); +export function createResponseTimeoutError(methodType: string, messageId: string) { + return new MagicSDKError( + SDKErrorCode.ResponseTimeout, + `Response timed out for method: ${methodType} with message id: ${messageId}`, + ); } export function createMalformedResponseError() { diff --git a/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx b/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx index 22f4fdfc7..b0ba1a958 100644 --- a/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx +++ b/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx @@ -236,8 +236,8 @@ export class ReactNativeWebViewController extends ViewController { // Setup timeout for message response const timeout = setTimeout(() => { this.messageTimeouts.delete(messageId); - throw createResponseTimeoutError(`Response timed out for method: ${methodType} with message id: ${messageId}`); - }, 5000); // 5-second timeout + throw createResponseTimeoutError(methodType, messageId); + }, 10000); // 10-second timeout this.messageTimeouts.set(messageId, timeout); diff --git a/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx b/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx index b160b1970..08b17565e 100644 --- a/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx +++ b/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx @@ -236,8 +236,8 @@ export class ReactNativeWebViewController extends ViewController { // Setup timeout for message response const timeout = setTimeout(() => { this.messageTimeouts.delete(messageId); - throw createResponseTimeoutError(`Response timed out for method: ${methodType} with message id: ${messageId}`); - }, 5000); // 5-second timeout + throw createResponseTimeoutError(methodType, messageId); + }, 10000); // 10-second timeout this.messageTimeouts.set(messageId, timeout); From db10277d61b41e2e3e7badd1161b8fe732bd6d6b Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Mon, 4 Mar 2024 11:33:08 -0800 Subject: [PATCH 06/16] Shrink code syntax --- .../react-native-bare/src/react-native-webview-controller.tsx | 2 +- .../react-native-expo/src/react-native-webview-controller.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx b/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx index b0ba1a958..1a2b1b597 100644 --- a/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx +++ b/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx @@ -142,7 +142,7 @@ export class ReactNativeWebViewController extends ViewController { const handleWebViewMessage = useCallback((event: any) => { const data = JSON.parse(event.nativeEvent.data); - if (data && data.response && data.response.id) { + if (data?.response?.id) { const messageId = data.response.id; // Clear timeout if message is received in time diff --git a/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx b/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx index 08b17565e..5e80e5d5e 100644 --- a/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx +++ b/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx @@ -142,7 +142,7 @@ export class ReactNativeWebViewController extends ViewController { const handleWebViewMessage = useCallback((event: any) => { const data = JSON.parse(event.nativeEvent.data); - if (data && data.response && data.response.id) { + if (data?.response?.id) { const messageId = data.response.id; // Clear timeout if message is received in time From a26d40cd7b47324e953be9f8d8db21c305f309d7 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Mon, 4 Mar 2024 13:53:03 -0800 Subject: [PATCH 07/16] Make messageId a number --- packages/@magic-sdk/provider/src/core/sdk-exceptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts b/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts index f5d1ee1a4..a480b0ba4 100644 --- a/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts +++ b/packages/@magic-sdk/provider/src/core/sdk-exceptions.ts @@ -110,7 +110,7 @@ export function createModalNotReadyError() { return new MagicSDKError(SDKErrorCode.ModalNotReady, 'Modal is not ready.'); } -export function createResponseTimeoutError(methodType: string, messageId: string) { +export function createResponseTimeoutError(methodType: string, messageId: number) { return new MagicSDKError( SDKErrorCode.ResponseTimeout, `Response timed out for method: ${methodType} with message id: ${messageId}`, From c9c56e6facaedf18441dec07cb3cd348afd4f023 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Mon, 4 Mar 2024 13:53:28 -0800 Subject: [PATCH 08/16] payload is optional --- .../src/react-native-webview-controller.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx b/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx index 1a2b1b597..17ddab89b 100644 --- a/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx +++ b/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx @@ -229,17 +229,20 @@ export class ReactNativeWebViewController extends ViewController { } protected async _post(data: any) { - const methodType = data.payload.method; - const messageId = data.payload.id; + // Safely access `method` and `id` from `payload`, defaulting to undefined if not present + const methodType = data.payload?.method; + const messageId = data.payload?.id; if (this.webView && (this.webView as any).postMessage) { // Setup timeout for message response - const timeout = setTimeout(() => { - this.messageTimeouts.delete(messageId); - throw createResponseTimeoutError(methodType, messageId); - }, 10000); // 10-second timeout + if (methodType && messageId) { + const timeout = setTimeout(() => { + this.messageTimeouts.delete(messageId); + throw createResponseTimeoutError(methodType, messageId); + }, 10000); // 10-second timeout - this.messageTimeouts.set(messageId, timeout); + this.messageTimeouts.set(messageId, timeout); + } (this.webView as any).postMessage( JSON.stringify(data, (key, value) => { From 466d5b3512d7a0fd1d730d58f79faceb1fcb91e2 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Mon, 4 Mar 2024 14:01:55 -0800 Subject: [PATCH 09/16] Update expo rn sdk to match bare --- .../src/react-native-webview-controller.tsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx b/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx index 5e80e5d5e..3b035de1a 100644 --- a/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx +++ b/packages/@magic-sdk/react-native-expo/src/react-native-webview-controller.tsx @@ -229,17 +229,20 @@ export class ReactNativeWebViewController extends ViewController { } protected async _post(data: any) { - const methodType = data.payload.method; - const messageId = data.payload.id; + // Safely access `method` and `id` from `payload`, defaulting to undefined if not present + const methodType = data.payload?.method; + const messageId = data.payload?.id; if (this.webView && (this.webView as any).postMessage) { // Setup timeout for message response - const timeout = setTimeout(() => { - this.messageTimeouts.delete(messageId); - throw createResponseTimeoutError(methodType, messageId); - }, 10000); // 10-second timeout + if (methodType && messageId) { + const timeout = setTimeout(() => { + this.messageTimeouts.delete(messageId); + throw createResponseTimeoutError(methodType, messageId); + }, 10000); // 10-second timeout - this.messageTimeouts.set(messageId, timeout); + this.messageTimeouts.set(messageId, timeout); + } (this.webView as any).postMessage( JSON.stringify(data, (key, value) => { From 8bafb75b097b833dd847299a3d01a907efac4255 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Mon, 4 Mar 2024 16:03:29 -0800 Subject: [PATCH 10/16] Update testing --- .../src/react-native-webview-controller.tsx | 1 + .../_post.spec.ts | 25 ++++++++++++++++++- .../_post.spec.ts | 25 ++++++++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx b/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx index 17ddab89b..0cdda873f 100644 --- a/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx +++ b/packages/@magic-sdk/react-native-bare/src/react-native-webview-controller.tsx @@ -238,6 +238,7 @@ export class ReactNativeWebViewController extends ViewController { if (methodType && messageId) { const timeout = setTimeout(() => { this.messageTimeouts.delete(messageId); + throw createResponseTimeoutError(methodType, messageId); }, 10000); // 10-second timeout diff --git a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts index f8ceaf4a1..4c67c2fe0 100644 --- a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts @@ -1,5 +1,5 @@ import browserEnv from '@ikscodes/browser-env'; -import { createModalNotReadyError } from '@magic-sdk/provider'; +import { MagicSDKError, createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider'; import { createReactNativeWebViewController } from '../../factories'; import { reactNativeStyleSheetStub } from '../../mocks'; @@ -50,3 +50,26 @@ test('Process Typed Array in a Solana Request', async () => { 'http://example.com', ]); }); + +// TODO: Figure out how to test this +test.skip('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { + jest.useFakeTimers(); + const overlay = createReactNativeWebViewController('http://example.com'); + + const postStub = jest.fn(); + overlay.webView = { postMessage: postStub }; + + // Setup expected payload and error + const payload = { method: 'testMethod', id: 123 }; + const expectedError = createResponseTimeoutError(payload.method, payload.id); + + const promise = await overlay._post({ payload }); + + // Fast-forward time by 10 seconds + jest.advanceTimersByTime(10000); + + // Assert that the promise rejects with the expected error + expect(promise).rejects.toThrow(expectedError); + + jest.useRealTimers(); +}); diff --git a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts index f8ceaf4a1..4c67c2fe0 100644 --- a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts @@ -1,5 +1,5 @@ import browserEnv from '@ikscodes/browser-env'; -import { createModalNotReadyError } from '@magic-sdk/provider'; +import { MagicSDKError, createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider'; import { createReactNativeWebViewController } from '../../factories'; import { reactNativeStyleSheetStub } from '../../mocks'; @@ -50,3 +50,26 @@ test('Process Typed Array in a Solana Request', async () => { 'http://example.com', ]); }); + +// TODO: Figure out how to test this +test.skip('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { + jest.useFakeTimers(); + const overlay = createReactNativeWebViewController('http://example.com'); + + const postStub = jest.fn(); + overlay.webView = { postMessage: postStub }; + + // Setup expected payload and error + const payload = { method: 'testMethod', id: 123 }; + const expectedError = createResponseTimeoutError(payload.method, payload.id); + + const promise = await overlay._post({ payload }); + + // Fast-forward time by 10 seconds + jest.advanceTimersByTime(10000); + + // Assert that the promise rejects with the expected error + expect(promise).rejects.toThrow(expectedError); + + jest.useRealTimers(); +}); From 1ba6bb5f40eb37149280d5dff27ba4a961550e2e Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Mon, 4 Mar 2024 16:13:57 -0800 Subject: [PATCH 11/16] Add testing to error factory --- .../test/spec/core/sdk-exceptions/error-factories.spec.ts | 6 ++++++ .../test/spec/react-native-webview-controller/_post.spec.ts | 2 +- .../test/spec/react-native-webview-controller/_post.spec.ts | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/@magic-sdk/provider/test/spec/core/sdk-exceptions/error-factories.spec.ts b/packages/@magic-sdk/provider/test/spec/core/sdk-exceptions/error-factories.spec.ts index 59070b14a..de8982094 100644 --- a/packages/@magic-sdk/provider/test/spec/core/sdk-exceptions/error-factories.spec.ts +++ b/packages/@magic-sdk/provider/test/spec/core/sdk-exceptions/error-factories.spec.ts @@ -35,6 +35,12 @@ test('Creates a `MODAL_NOT_READY` error', async () => { errorAssertions(error, 'MODAL_NOT_READY', 'Modal is not ready.'); }); +test('Creates a `RESPONSE_TIMEOUT` error', async () => { + const { createResponseTimeoutError } = require('../../../../src/core/sdk-exceptions'); + const error = createResponseTimeoutError('test_method', 123); + errorAssertions(error, 'RESPONSE_TIMEOUT', 'Response timed out for method: test_method with message id: 123'); +}); + test('Creates a `MALFORMED_RESPONSE` error', async () => { const { createMalformedResponseError } = require('../../../../src/core/sdk-exceptions'); const error = createMalformedResponseError(); diff --git a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts index 4c67c2fe0..a90e52608 100644 --- a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts @@ -1,5 +1,5 @@ import browserEnv from '@ikscodes/browser-env'; -import { MagicSDKError, createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider'; +import { createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider'; import { createReactNativeWebViewController } from '../../factories'; import { reactNativeStyleSheetStub } from '../../mocks'; diff --git a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts index 4c67c2fe0..a90e52608 100644 --- a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts @@ -1,5 +1,5 @@ import browserEnv from '@ikscodes/browser-env'; -import { MagicSDKError, createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider'; +import { createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider'; import { createReactNativeWebViewController } from '../../factories'; import { reactNativeStyleSheetStub } from '../../mocks'; From 88221d5252ac3e290fdef95fa9c532c2fb2b4c96 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Fri, 8 Mar 2024 12:27:14 -0800 Subject: [PATCH 12/16] Update testing --- .../spec/react-native-webview-controller/_post.spec.ts | 7 +++---- .../spec/react-native-webview-controller/_post.spec.ts | 7 +++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts index a90e52608..94012de95 100644 --- a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts @@ -51,8 +51,7 @@ test('Process Typed Array in a Solana Request', async () => { ]); }); -// TODO: Figure out how to test this -test.skip('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { +test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { jest.useFakeTimers(); const overlay = createReactNativeWebViewController('http://example.com'); @@ -63,13 +62,13 @@ test.skip('Throws RESPONSE_TIMEOUT error if response takes longer than 10 second const payload = { method: 'testMethod', id: 123 }; const expectedError = createResponseTimeoutError(payload.method, payload.id); - const promise = await overlay._post({ payload }); + const promise = overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); // Fast-forward time by 10 seconds jest.advanceTimersByTime(10000); // Assert that the promise rejects with the expected error - expect(promise).rejects.toThrow(expectedError); + await expect(() => promise).rejects.toThrow(expectedError); jest.useRealTimers(); }); diff --git a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts index a90e52608..94012de95 100644 --- a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts @@ -51,8 +51,7 @@ test('Process Typed Array in a Solana Request', async () => { ]); }); -// TODO: Figure out how to test this -test.skip('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { +test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { jest.useFakeTimers(); const overlay = createReactNativeWebViewController('http://example.com'); @@ -63,13 +62,13 @@ test.skip('Throws RESPONSE_TIMEOUT error if response takes longer than 10 second const payload = { method: 'testMethod', id: 123 }; const expectedError = createResponseTimeoutError(payload.method, payload.id); - const promise = await overlay._post({ payload }); + const promise = overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); // Fast-forward time by 10 seconds jest.advanceTimersByTime(10000); // Assert that the promise rejects with the expected error - expect(promise).rejects.toThrow(expectedError); + await expect(() => promise).rejects.toThrow(expectedError); jest.useRealTimers(); }); From 13354539b407f24869b2f082b3fa5abf4ae18823 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Fri, 8 Mar 2024 14:00:40 -0800 Subject: [PATCH 13/16] WIP --- .../_post.spec.ts | 19 ++++++++++++------- .../_post.spec.ts | 13 ++++++++++--- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts index 94012de95..876447549 100644 --- a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts @@ -1,5 +1,6 @@ import browserEnv from '@ikscodes/browser-env'; import { createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider'; +import { SDKErrorCode } from '@magic-sdk/types'; import { createReactNativeWebViewController } from '../../factories'; import { reactNativeStyleSheetStub } from '../../mocks'; @@ -53,22 +54,26 @@ test('Process Typed Array in a Solana Request', async () => { test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { jest.useFakeTimers(); - const overlay = createReactNativeWebViewController('http://example.com'); + const overlay = createReactNativeWebViewController('http://example.com'); const postStub = jest.fn(); overlay.webView = { postMessage: postStub }; - // Setup expected payload and error + // Assume `_post` method returns a promise that rejects upon timeout const payload = { method: 'testMethod', id: 123 }; - const expectedError = createResponseTimeoutError(payload.method, payload.id); - - const promise = overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); + await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); - // Fast-forward time by 10 seconds + // Simulate advancing time by 10 seconds to trigger the timeout jest.advanceTimersByTime(10000); // Assert that the promise rejects with the expected error - await expect(() => promise).rejects.toThrow(expectedError); + console.log(postStub.mock.calls[0]); + expect(postStub.mock.calls[0]).toEqual( + expect.objectContaining({ + code: SDKErrorCode.ResponseTimeout, + message: expect.stringContaining('Response timed out for method: testMethod with message id: 123'), + }), + ); jest.useRealTimers(); }); diff --git a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts index 94012de95..f53493abd 100644 --- a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts @@ -1,5 +1,6 @@ import browserEnv from '@ikscodes/browser-env'; import { createModalNotReadyError, createResponseTimeoutError } from '@magic-sdk/provider'; +import { SDKErrorCode } from '@magic-sdk/types'; import { createReactNativeWebViewController } from '../../factories'; import { reactNativeStyleSheetStub } from '../../mocks'; @@ -53,14 +54,15 @@ test('Process Typed Array in a Solana Request', async () => { test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { jest.useFakeTimers(); + jest.spyOn(global, 'setTimeout'); + const overlay = createReactNativeWebViewController('http://example.com'); const postStub = jest.fn(); overlay.webView = { postMessage: postStub }; - // Setup expected payload and error + // Setup expected payload const payload = { method: 'testMethod', id: 123 }; - const expectedError = createResponseTimeoutError(payload.method, payload.id); const promise = overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); @@ -68,7 +70,12 @@ test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', a jest.advanceTimersByTime(10000); // Assert that the promise rejects with the expected error - await expect(() => promise).rejects.toThrow(expectedError); + await expect(promise).rejects.toEqual( + expect.objectContaining({ + code: SDKErrorCode.ResponseTimeout, + message: expect.stringContaining('Response timed out for method: testMethod with message id: 123'), + }), + ); jest.useRealTimers(); }); From 80a2443c529ae04abd0945028bafc04c719b6cd5 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Fri, 8 Mar 2024 14:31:44 -0800 Subject: [PATCH 14/16] Fix tests --- .../_post.spec.ts | 22 +++++----------- .../_post.spec.ts | 25 +++++-------------- 2 files changed, 12 insertions(+), 35 deletions(-) diff --git a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts index 876447549..cc6165622 100644 --- a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts @@ -53,27 +53,17 @@ test('Process Typed Array in a Solana Request', async () => { }); test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { - jest.useFakeTimers(); - const overlay = createReactNativeWebViewController('http://example.com'); + const postStub = jest.fn(); overlay.webView = { postMessage: postStub }; // Assume `_post` method returns a promise that rejects upon timeout const payload = { method: 'testMethod', id: 123 }; - await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); - - // Simulate advancing time by 10 seconds to trigger the timeout - jest.advanceTimersByTime(10000); - - // Assert that the promise rejects with the expected error - console.log(postStub.mock.calls[0]); - expect(postStub.mock.calls[0]).toEqual( - expect.objectContaining({ - code: SDKErrorCode.ResponseTimeout, - message: expect.stringContaining('Response timed out for method: testMethod with message id: 123'), - }), - ); - jest.useRealTimers(); + try { + await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); + } catch (error) { + expect(error.code).toBe(SDKErrorCode.ResponseTimeout); + } }); diff --git a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts index f53493abd..876924d0f 100644 --- a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts @@ -53,29 +53,16 @@ test('Process Typed Array in a Solana Request', async () => { }); test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { - jest.useFakeTimers(); - jest.spyOn(global, 'setTimeout'); - const overlay = createReactNativeWebViewController('http://example.com'); - const postStub = jest.fn(); overlay.webView = { postMessage: postStub }; - // Setup expected payload + // Assume `_post` method returns a promise that rejects upon timeout const payload = { method: 'testMethod', id: 123 }; - const promise = overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); - - // Fast-forward time by 10 seconds - jest.advanceTimersByTime(10000); - - // Assert that the promise rejects with the expected error - await expect(promise).rejects.toEqual( - expect.objectContaining({ - code: SDKErrorCode.ResponseTimeout, - message: expect.stringContaining('Response timed out for method: testMethod with message id: 123'), - }), - ); - - jest.useRealTimers(); + try { + await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); + } catch (error) { + expect(error.code).toBe(SDKErrorCode.ResponseTimeout); + } }); From 7fd6ace608225150dd0154a4931d55fbda3f629e Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Fri, 8 Mar 2024 15:03:11 -0800 Subject: [PATCH 15/16] Additional testing --- .../spec/core/view-controller/post.spec.ts | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/@magic-sdk/provider/test/spec/core/view-controller/post.spec.ts b/packages/@magic-sdk/provider/test/spec/core/view-controller/post.spec.ts index 74a83f6dd..80c93bba8 100644 --- a/packages/@magic-sdk/provider/test/spec/core/view-controller/post.spec.ts +++ b/packages/@magic-sdk/provider/test/spec/core/view-controller/post.spec.ts @@ -3,13 +3,14 @@ import browserEnv from '@ikscodes/browser-env'; import { MagicIncomingWindowMessage, MagicOutgoingWindowMessage, JsonRpcRequestPayload } from '@magic-sdk/types'; +import { create } from 'lodash'; import { createViewController, TestViewController } from '../../../factories'; import { JsonRpcResponse } from '../../../../src/core/json-rpc'; import * as storage from '../../../../src/util/storage'; import * as webCryptoUtils from '../../../../src/util/web-crypto'; import * as deviceShareWebCryptoUtils from '../../../../src/util/device-share-web-crypto'; import { SDKEnvironment } from '../../../../src/core/sdk-environment'; -import { createModalNotReadyError } from '../../../../src/core/sdk-exceptions'; +import { createModalNotReadyError, createResponseTimeoutError } from '../../../../src/core/sdk-exceptions'; /** * Create a dummy request payload. @@ -262,6 +263,24 @@ test('throws MODAL_NOT_READY error when not connected to the internet', async () } }); +test('throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', async () => { + const eventWithRt = { data: { ...responseEvent().data } }; + const { handlerSpy, onSpy } = stubViewController(viewController, [ + [MagicIncomingWindowMessage.MAGIC_HANDLE_RESPONSE, eventWithRt], + ]); + + // @ts-ignore protected variable + viewController.responseTimeout = 1; + + const payload = requestPayload(); + + try { + await viewController.post(MagicOutgoingWindowMessage.MAGIC_HANDLE_REQUEST, payload); + } catch (e) { + expect(e).toEqual(createResponseTimeoutError('eth_accounts', 1)); + } +}); + test('does not call web crypto api if platform is not web', async () => { SDKEnvironment.platform = 'react-native'; const eventWithRt = { data: { ...responseEvent().data } }; From bff572a227ff20f14112b8287917abf94da6b1c0 Mon Sep 17 00:00:00 2001 From: Arian Flores Date: Fri, 8 Mar 2024 17:19:28 -0800 Subject: [PATCH 16/16] Additional testing --- .../_post.spec.ts | 69 +++++++++++++++++++ .../_post.spec.ts | 69 +++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts index cc6165622..d0d41c32a 100644 --- a/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-bare/test/spec/react-native-webview-controller/_post.spec.ts @@ -67,3 +67,72 @@ test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', a expect(error.code).toBe(SDKErrorCode.ResponseTimeout); } }); + +test('Adds a timeout to messageTimeouts map on message send', async () => { + const overlay = createReactNativeWebViewController('http://example.com'); + + overlay.webView = { postMessage: jest.fn() }; + const payload = { method: 'testMethod', id: 123 }; + + try { + await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); + } catch (error) { + expect(error.code).toBe(SDKErrorCode.ResponseTimeout); + } + + expect(overlay.messageTimeouts.has(123)).toBe(true); +}); + +test('Removes timeout from messageTimeouts map on response', async () => { + const overlay = createReactNativeWebViewController('http://example.com'); + + // Mock the WebView and its postMessage method + overlay.webView = { postMessage: jest.fn() }; + + const payload = { method: 'testMethod', id: 123 }; + try { + await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); + } catch (error) { + expect(error.code).toBe(SDKErrorCode.ResponseTimeout); + } + + try { + // Simulate receiving a response by directly invoking the message handler + overlay.handleReactNativeWebViewMessage({ + nativeEvent: { + data: JSON.stringify({ response: { id: 123 } }), + }, + }); + } catch (e) { + console.log(e); + } + + expect(overlay.messageTimeouts.has(123)).toBe(true); +}); + +test('Removes timeout from messageTimeouts map on timeout', async () => { + expect.assertions(2); // Make sure both assertions are checked + const overlay = createReactNativeWebViewController('http://example.com'); + + overlay.webView = { postMessage: jest.fn() }; + + // Mock setTimeout to immediately invoke its callback to simulate a timeout + jest.spyOn(global, 'setTimeout').mockImplementationOnce((cb) => { + cb(); + return 123; // Return a mock timer ID + }); + + const payload = { method: 'testMethod', id: 123 }; + + try { + await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); + } catch (error) { + // Expect that the error was thrown due to a timeout + expect(error.code).toBe(SDKErrorCode.ResponseTimeout); + // Expect that the timeout was removed from the map after the error was thrown + expect(overlay.messageTimeouts.has(123)).toBe(false); + } + + // Restore original setTimeout function + jest.restoreAllMocks(); +}); diff --git a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts index 876924d0f..00d15097b 100644 --- a/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts +++ b/packages/@magic-sdk/react-native-expo/test/spec/react-native-webview-controller/_post.spec.ts @@ -66,3 +66,72 @@ test('Throws RESPONSE_TIMEOUT error if response takes longer than 10 seconds', a expect(error.code).toBe(SDKErrorCode.ResponseTimeout); } }); + +test('Adds a timeout to messageTimeouts map on message send', async () => { + const overlay = createReactNativeWebViewController('http://example.com'); + + overlay.webView = { postMessage: jest.fn() }; + const payload = { method: 'testMethod', id: 123 }; + + try { + await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); + } catch (error) { + expect(error.code).toBe(SDKErrorCode.ResponseTimeout); + } + + expect(overlay.messageTimeouts.has(123)).toBe(true); +}); + +test('Removes timeout from messageTimeouts map on response', async () => { + const overlay = createReactNativeWebViewController('http://example.com'); + + // Mock the WebView and its postMessage method + overlay.webView = { postMessage: jest.fn() }; + + const payload = { method: 'testMethod', id: 123 }; + try { + await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); + } catch (error) { + expect(error.code).toBe(SDKErrorCode.ResponseTimeout); + } + + try { + // Simulate receiving a response by directly invoking the message handler + overlay.handleReactNativeWebViewMessage({ + nativeEvent: { + data: JSON.stringify({ response: { id: 123 } }), + }, + }); + } catch (e) { + console.log(e); + } + + expect(overlay.messageTimeouts.has(123)).toBe(true); +}); + +test('Removes timeout from messageTimeouts map on timeout', async () => { + expect.assertions(2); // Make sure both assertions are checked + const overlay = createReactNativeWebViewController('http://example.com'); + + overlay.webView = { postMessage: jest.fn() }; + + // Mock setTimeout to immediately invoke its callback to simulate a timeout + jest.spyOn(global, 'setTimeout').mockImplementationOnce((cb) => { + cb(); + return 123; // Return a mock timer ID + }); + + const payload = { method: 'testMethod', id: 123 }; + + try { + await overlay._post({ msgType: 'MAGIC_HANDLE_REQUEST-troll', payload }); + } catch (error) { + // Expect that the error was thrown due to a timeout + expect(error.code).toBe(SDKErrorCode.ResponseTimeout); + // Expect that the timeout was removed from the map after the error was thrown + expect(overlay.messageTimeouts.has(123)).toBe(false); + } + + // Restore original setTimeout function + jest.restoreAllMocks(); +});