UNPKG

@coinbase/onchaintestkit

Version:

End-to-end testing toolkit for blockchain applications, powered by Playwright

170 lines (169 loc) 7.02 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PhantomFixturesBuilder = PhantomFixturesBuilder; const test_1 = require("@playwright/test"); const _1 = require("."); const LocalNodeManager_1 = require("../../node/LocalNodeManager"); const createTempDir_1 = require("../../utils/createTempDir"); const extensionManager_1 = require("../../utils/extensionManager"); const removeTempDir_1 = require("../../utils/removeTempDir"); let sharedPhantomPage; // Constants const PHANTOM_API_URL = "https://api.phantom.app/portal/v1/public-apps**"; const PHANTOM_NODE_PROXY = "node-proxy.phantom.app"; // EXPERIMENTAL: Service Worker network interception (requires PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1) // See: https://playwright.dev/docs/network#service-worker-network-events // To enable: set environment variable PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1 async function setupPhantomNetworkInterception(context, localNodePort) { // Intercept ALL requests, including those from service workers (if experimental flag is enabled) await context.route("**", async (route) => { const request = route.request(); // Check if this request originated from a service worker if (request.serviceWorker()) { const postData = request.postData(); // Optionally, reroute to local node if it's a node-proxy call if (request.url().includes(PHANTOM_NODE_PROXY)) { try { const localUrl = `http://localhost:${localNodePort}`; const response = await context.request.post(localUrl, { data: postData, headers: { "Content-Type": "application/json" }, }); const responseBody = await response.text(); await route.fulfill({ status: response.status(), headers: response.headers(), body: responseBody, }); return; } catch (error) { console.error("[SW] Error rerouting service worker request:", error); } } // Otherwise, continue as normal await route.continue(); } else { // Not a service worker request, handle as before await route.continue(); } }); // Mock Phantom's domain validation API to allow localhost await context.route(PHANTOM_API_URL, async (route) => { await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify({ data: { apps: [{ domain: "localhost", verified: true }], domains: ["localhost"], }, }), }); }); console.log("[Phantom Network] Service worker interception enabled (experimental)"); } function PhantomFixturesBuilder(walletConfig, nodeConfig) { return test_1.test.extend({ // Add node fixture that will start before any wallet setup node: nodeConfig ? [ async ({}, use) => { try { const node = new LocalNodeManager_1.LocalNodeManager(nodeConfig); await node.start(); console.log(`Node is ready on port ${node.port}`); await use(node); console.log("Node stopping..."); await node.stop(); } catch (error) { console.error("Error in node fixture:", error); throw error; } }, { scope: "test", auto: true }, ] : undefined, _contextPath: async ({}, use, testInfo) => { const contextPath = await (0, createTempDir_1.createTempDir)(testInfo.testId); await use(contextPath); const error = await (0, removeTempDir_1.removeTempDir)(contextPath); if (error) { console.error(error); } }, context: async ({ context: currentContext, _contextPath, node }, use) => { try { const { phantomPage, phantomContext } = await _1.PhantomWallet.initialize(currentContext, _contextPath, walletConfig); sharedPhantomPage = phantomPage; // CRITICAL: Set up Phantom-specific network interception if (node?.port) { await setupPhantomNetworkInterception(phantomContext, node.port); } await use(phantomContext); await phantomContext.close(); } catch (error) { console.error("Error in context fixture:", error); throw error; } }, phantomPage: async ({ context: _ }, use) => { await use(sharedPhantomPage); }, extensionId: async ({ context }, use) => { try { const extensionId = await (0, extensionManager_1.getExtensionId)(context, "Phantom"); await use(extensionId); } catch (error) { console.error("Error in extensionId fixture:", error); throw error; } }, phantom: [ async ({ context, extensionId }, use) => { try { const phantom = new _1.PhantomWallet(walletConfig, context, sharedPhantomPage, extensionId); await use(phantom); } catch (error) { console.error("Error in phantom fixture:", error); throw error; } }, { scope: "test", auto: true }, ], setupWallet: [ async ({ phantom, node }, use) => { try { console.log("Setting up Phantom wallet..."); if (walletConfig.walletSetup) { await walletConfig.walletSetup(phantom, { localNodePort: node?.port || 8545, }); } console.log("Phantom wallet setup complete"); await use(null); } catch (error) { console.error("Error during wallet setup:", error); throw error; } }, { scope: "test", auto: true }, ], page: async ({ page }, use) => { try { await page.goto("/"); await use(page); } catch (error) { console.error("Error in page fixture:", error); throw error; } }, }); }