Skip to content

Commit 77ed12e

Browse files
test(e2e): route snaps webview traffic through mock server
Co-authored-by: javiergarciavera <javiergarciavera@users.noreply.github.com>
1 parent d6fd3c4 commit 77ed12e

File tree

3 files changed

+91
-0
lines changed

3 files changed

+91
-0
lines changed

app/lib/snaps/SnapsExecutionWebView.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,83 @@ import { PostMessageEvent } from '@metamask/post-message-stream';
99
// @ts-expect-error Types are currently broken for this.
1010
import WebViewHTML from '@metamask/snaps-execution-environments/dist/webpack/webview/index.html';
1111
import { EmptyObject } from '@metamask/snaps-sdk';
12+
import {
13+
FALLBACK_MOCK_SERVER_PORT,
14+
isE2E,
15+
testConfig,
16+
} from '../../util/test/utils';
1217

1318
const styles = createStyles();
1419

20+
const getE2ENetworkProxyInjection = (mockServerPort: string): string => `
21+
(function() {
22+
var mockServerUrl = 'http://localhost:${mockServerPort}';
23+
var proxyPrefix = mockServerUrl + '/proxy?url=';
24+
25+
function toUrlString(url) {
26+
if (typeof url === 'string') {
27+
return url;
28+
}
29+
if (url && typeof url === 'object' && typeof url.url === 'string') {
30+
return url.url;
31+
}
32+
return String(url);
33+
}
34+
35+
function shouldProxy(url) {
36+
return (
37+
typeof url === 'string' &&
38+
(url.startsWith('http://') || url.startsWith('https://')) &&
39+
!url.includes('/proxy?url=') &&
40+
!url.includes('localhost:${mockServerPort}') &&
41+
!url.includes('127.0.0.1:${mockServerPort}') &&
42+
!url.includes('10.0.2.2:${mockServerPort}')
43+
);
44+
}
45+
46+
if (typeof fetch === 'function') {
47+
var originalFetch = fetch.bind(window);
48+
window.fetch = function(url, options) {
49+
var urlString = toUrlString(url);
50+
var finalUrl = shouldProxy(urlString)
51+
? proxyPrefix + encodeURIComponent(urlString)
52+
: url;
53+
return originalFetch(finalUrl, options);
54+
};
55+
}
56+
57+
var OriginalXHR = window.XMLHttpRequest;
58+
if (typeof OriginalXHR === 'function') {
59+
window.XMLHttpRequest = function() {
60+
var xhr = new OriginalXHR();
61+
var originalOpen = xhr.open;
62+
63+
xhr.open = function(method, url) {
64+
var openArgs = Array.prototype.slice.call(arguments, 2);
65+
var finalUrl = shouldProxy(url)
66+
? proxyPrefix + encodeURIComponent(url)
67+
: url;
68+
return originalOpen.call.apply(
69+
originalOpen,
70+
[this, method, finalUrl].concat(openArgs),
71+
);
72+
};
73+
74+
return xhr;
75+
};
76+
77+
try {
78+
Object.setPrototypeOf(window.XMLHttpRequest, OriginalXHR);
79+
Object.assign(window.XMLHttpRequest, OriginalXHR);
80+
window.XMLHttpRequest.prototype = OriginalXHR.prototype;
81+
} catch (e) {
82+
// No-op: this is best effort in the constrained WebView runtime.
83+
}
84+
}
85+
})();
86+
true;
87+
`;
88+
1589
// This is a hack to allow us to asynchronously await the creation of the WebView.
1690
// eslint-disable-next-line import/no-mutable-exports
1791
export let createWebView: (jobId: string) => Promise<WebViewInterface>;
@@ -107,6 +181,14 @@ export class SnapsExecutionWebView extends Component {
107181
}
108182

109183
render() {
184+
const mockServerPort = String(
185+
testConfig.mockServerPort ?? FALLBACK_MOCK_SERVER_PORT,
186+
);
187+
const e2eWebViewNetworkProxyInjection = isE2E
188+
? getE2ENetworkProxyInjection(mockServerPort)
189+
: undefined;
190+
const mixedContentMode = isE2E ? 'always' : undefined;
191+
110192
return (
111193
<View style={styles.container}>
112194
{Object.entries(this.webViews).map(([key, { props }]) => (
@@ -119,6 +201,10 @@ export class SnapsExecutionWebView extends Component {
119201
onError={props.onWebViewError}
120202
onLoadEnd={props.onWebViewLoad}
121203
originWhitelist={['*']}
204+
mixedContentMode={mixedContentMode}
205+
injectedJavaScriptBeforeContentLoaded={
206+
e2eWebViewNetworkProxyInjection
207+
}
122208
javaScriptEnabled
123209
webviewDebuggingEnabled={__DEV__}
124210
/>

app/util/test/utils.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const flushPromises = () => new Promise(setImmediate);
55
// iOS: These are overridden by LaunchArgs at runtime
66
export const FALLBACK_FIXTURE_SERVER_PORT = 12345;
77
export const FALLBACK_COMMAND_QUEUE_SERVER_PORT = 2446;
8+
export const FALLBACK_MOCK_SERVER_PORT = 8000;
89

910
// E2E test configuration required in app
1011
export const testConfig = {};

shim.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { LaunchArguments } from 'react-native-launch-arguments';
99
import {
1010
FALLBACK_FIXTURE_SERVER_PORT,
1111
FALLBACK_COMMAND_QUEUE_SERVER_PORT,
12+
FALLBACK_MOCK_SERVER_PORT,
1213
isE2E,
1314
isTest,
1415
enableApiCallLogs,
@@ -66,6 +67,9 @@ if (isTest) {
6667
testConfig.commandQueueServerPort = raw?.commandQueueServerPort
6768
? raw.commandQueueServerPort
6869
: FALLBACK_COMMAND_QUEUE_SERVER_PORT;
70+
testConfig.mockServerPort = raw?.mockServerPort
71+
? raw.mockServerPort
72+
: FALLBACK_MOCK_SERVER_PORT;
6973
}
7074

7175
// Fix for https://github.com/facebook/react-native/issues/5667

0 commit comments

Comments
 (0)