import * as chai from "chai"; import chaiAsPromised from "chai-as-promised"; import puppeteer from "puppeteer"; import { spawn } from "child_process"; import { register } from "ts-node"; import { env } from "process"; chai.use(chaiAsPromised); register({ project: "./tsconfig.json", }); let serverProcess; let browser; let testingPage; const TEST_SERVER_PORT = 8080; const TEST_SERVER = `http://localhost:${TEST_SERVER_PORT}`; // Should be in sync with the rpc port in tests/config/miden-node.toml const LOCAL_MIDEN_NODE_PORT = 57291; const REMOTE_TX_PROVER_PORT = 50051; before(async () => { console.log("Starting test server..."); serverProcess = spawn("http-server", ["./dist", "-p", TEST_SERVER_PORT], { stdio: "inherit", shell: process.platform == "win32", }); try { browser = await puppeteer.launch({ headless: true, protocolTimeout: 360000, }); testingPage = await browser.newPage(); await testingPage.goto(TEST_SERVER); } catch (error) { console.error("Failed to launch Puppeteer:", error); if (serverProcess && !serverProcess.killed) { serverProcess.kill("SIGTERM"); } throw error; } testingPage.on("console", (msg) => console.log("PAGE LOG:", msg.text())); testingPage.on("pageerror", (err) => { console.error("PAGE ERROR:", err); }); testingPage.on("error", (err) => { console.error("PUPPETEER ERROR:", err); }); // Creates the client in the test context and attach to window object await testingPage.evaluate( async (rpc_port, remote_prover_port) => { const { Account, AccountHeader, AccountId, AccountStorageMode, AdviceMap, AuthSecretKey, ConsumableNoteRecord, Felt, FeltArray, FungibleAsset, Note, NoteAssets, NoteConsumability, NoteExecutionHint, NoteExecutionMode, NoteFilter, NoteFilterTypes, NoteIdAndArgs, NoteIdAndArgsArray, NoteInputs, NoteMetadata, NoteRecipient, NoteScript, NoteTag, NoteType, OutputNote, OutputNotesArray, Rpo256, TestUtils, TransactionFilter, TransactionProver, TransactionRequest, TransactionResult, TransactionRequestBuilder, TransactionScriptInputPair, TransactionScriptInputPairArray, Word, WebClient, } = await import("./index.js"); let rpc_url = `http://localhost:${rpc_port}`; let prover_url = null; if (remote_prover_port) { prover_url = `http://localhost:${remote_prover_port}`; } const client = await WebClient.create_client(rpc_url); window.client = client; window.Account = Account; window.AccountHeader = AccountHeader; window.AccountId = AccountId; window.AccountStorageMode = AccountStorageMode; window.AdviceMap = AdviceMap; window.AuthSecretKey = AuthSecretKey; window.ConsumableNoteRecord = ConsumableNoteRecord; window.Felt = Felt; window.FeltArray = FeltArray; window.FungibleAsset = FungibleAsset; window.Note = Note; window.NoteAssets = NoteAssets; window.NoteConsumability = NoteConsumability; window.NoteExecutionHint = NoteExecutionHint; window.NoteExecutionMode = NoteExecutionMode; window.NoteFilter = NoteFilter; window.NoteFilterTypes = NoteFilterTypes; window.NoteIdAndArgs = NoteIdAndArgs; window.NoteIdAndArgsArray = NoteIdAndArgsArray; window.NoteInputs = NoteInputs; window.NoteMetadata = NoteMetadata; window.NoteRecipient = NoteRecipient; window.NoteScript = NoteScript; window.NoteTag = NoteTag; window.NoteType = NoteType; window.OutputNote = OutputNote; window.OutputNotesArray = OutputNotesArray; window.Rpo256 = Rpo256; window.TestUtils = TestUtils; window.TransactionFilter = TransactionFilter; window.TransactionProver = TransactionProver; window.TransactionRequest = TransactionRequest; window.TransactionResult = TransactionResult; window.TransactionRequestBuilder = TransactionRequestBuilder; window.TransactionScriptInputPair = TransactionScriptInputPair; window.TransactionScriptInputPairArray = TransactionScriptInputPairArray; window.WebClient = WebClient; window.Word = Word; // Create a namespace for helper functions window.helpers = window.helpers || {}; // Add the remote prover url to window window.remote_prover_url = prover_url; window.helpers.waitForTransaction = async ( transactionId, maxWaitTime = 20000, delayInterval = 1000 ) => { const client = window.client; let timeWaited = 0; while (true) { if (timeWaited >= maxWaitTime) { throw new Error("Timeout waiting for transaction"); } await client.sync_state(); const uncomittedTransactions = await client.get_transactions( window.TransactionFilter.uncomitted() ); let uncomittedTransactionIds = uncomittedTransactions.map( (transaction) => transaction.id().to_hex() ); if (!uncomittedTransactionIds.includes(transactionId)) { break; } await new Promise((r) => setTimeout(r, delayInterval)); timeWaited += delayInterval; } }; window.helpers.refreshClient = async (initSeed) => { const client = await WebClient.create_client(rpc_url, initSeed); window.client = client; }; }, LOCAL_MIDEN_NODE_PORT, env.REMOTE_PROVER ? REMOTE_TX_PROVER_PORT : null ); }); beforeEach(async () => { await testingPage.evaluate(async () => { // Open a connection to the list of databases const databases = await indexedDB.databases(); for (const db of databases) { // Delete each database by name indexedDB.deleteDatabase(db.name); } }); }); after(async () => { console.log("Stopping test server..."); console.log("Closing browser..."); await browser.close(); console.log("Browser closed."); console.log("Beginning server process cleanup..."); if (serverProcess && !serverProcess.killed) { console.log("Killing server process..."); serverProcess.kill("SIGTERM"); // Send the SIGTERM signal to terminate the server } console.log("Waiting for server process to exit..."); await new Promise((resolve, reject) => { if (serverProcess.exitCode !== null) { // Process has already exited, resolve immediately console.log( `Server process had already exited with code ${serverProcess.exitCode}` ); return resolve(); } serverProcess.on("close", (code) => { console.log(`Server process exited with code ${code}`); resolve(); }); serverProcess.on("error", (error) => { console.error("Error killing server process:", error); reject(error); }); }); console.log("Test server stopped."); }); export { testingPage };