UNPKG

@moonwall/cli

Version:

Testing framework for the Moon family of projects

165 lines 7.06 kB
import "@moonbeam-network/api-augment"; import Bottleneck from "bottleneck"; import { createLogger } from "@moonwall/util"; import { afterAll, beforeAll, describe, it } from "vitest"; import { getEnvironmentFromConfig } from "./configReader"; import { MoonwallContext, contextCreator } from "./globalContext"; import { chopsticksHandler } from "./handlers/chopsticksHandler"; import { devHandler } from "./handlers/devHandler"; import { readOnlyHandler } from "./handlers/readOnlyHandler"; import { zombieHandler } from "./handlers/zombieHandler"; const RT_VERSION = Number(process.env.MOON_RTVERSION); const RT_NAME = process.env.MOON_RTNAME; let limiter = undefined; // About: This has been designed in the handler pattern so that eventually we can integrate it to vitest // https://vitest.dev/advanced/runner.html /** * * Defines a suite of tests based on provided parameters. * * @param {object} params - The setup parameters for the test suite. * @param {string} params.id - A unique identifier for the test suite (e.g. D03). * @param {string} params.title - The title of the test suite (e.g. 'Fee calculation: congestion handling'). * @param {function} params.testCases - A callback function that houses the individual test cases of this suite. * @param {string} params.foundationMethods - Explicitly specify which foundation these tests will run against reveal which methods to make available. * @param {number} [params.minRtVersion] - The minimum runtime version required for the test suite, otherwise will be skipped. * @param {string} [params.chainType] - The required runtime name required for the test suite, otherwise will be skipped. * @param {string} [params.notChainType] - The runtime name to not run against this test suite, otherwise will not be skipped. * * @returns {void} - No explicit return value, this function results is wrapped and handled by the vitest instance. * @example * describeSuite({ * id: "D01", * title: "Sample test suite", * foundationMethods: "dev", * testCases: ({ it, context, log }) => { * it({ * id: "T01", * title: "Sample test case", * testds: async function () { * expect(true).to.be.true; * }, * }); * }, * }); */ export function describeSuite({ id: suiteId, title, testCases, foundationMethods, minRtVersion, chainType, notChainType, options, }) { if ((minRtVersion && minRtVersion > RT_VERSION) || (chainType && chainType !== RT_NAME) || (notChainType && notChainType === RT_NAME)) { describe.skip(`🗃️ ${suiteId} ${title}`); return; } let ctx = null; const logger = createLogger({ name: suiteId }); beforeAll(async () => { const env = getEnvironmentFromConfig(); if (env.foundation.type === "dev") { // Pass options to contextCreator if they exist ctx = await contextCreator(options); } ctx = await contextCreator(); if (env.foundation.type === "read_only") { const settings = loadParams(env.foundation.launchSpec); limiter = new Bottleneck(settings); } }); afterAll(async () => { // The termination reason will be set by the test runner or signal handlers // Default to "tests finished" if not set explicitly const reason = global.MOONWALL_TERMINATION_REASON || "tests finished"; await MoonwallContext.destroy(reason); ctx = null; }); const testCase = (params) => { if (params.modifier) { it[params.modifier](`📁 ${suiteId.concat(params.id)} ${params.title}`, params.test, params.timeout); return; } if ((params.minRtVersion && params.minRtVersion > RT_VERSION) || (params.chainType && params.chainType !== RT_NAME) || (params.notChainType && params.notChainType === RT_NAME)) { it.skip(`📁 ${suiteId.concat(params.id)} ${params.title}`, params.test, params.timeout); return; } it(`📁 ${suiteId.concat(params.id)} ${params.title}`, params.test, params.timeout); }; describe(`🗃️ ${suiteId} ${title}`, () => { const getApi = (apiType, apiName) => { if (!ctx) { throw new Error("Context not initialized"); } const provider = ctx.providers.find((prov) => { if (apiType && apiName) { return prov.type === apiType && prov.name === apiName; } if (apiType && !apiName) { return prov.type === apiType; } if (!apiType && apiName) { return prov.name === apiName; } return false; }); if (!provider) { throw new Error(`API of type ${apiType} ${apiName ? `and name ${apiName}` : ""} could not be found`); } return !limiter ? provider.api : scheduleWithBottleneck(provider.api); }; const context = { api: (type, name) => getApi(type, name), viem: (apiName) => getApi("viem", apiName), polkadotJs: (apiName) => getApi("polkadotJs", apiName), ethers: (apiName) => getApi("ethers", apiName), web3: (apiName) => getApi("web3", apiName), papi: (apiName) => getApi("papi", apiName), }; const foundationHandlers = { dev: devHandler, chopsticks: chopsticksHandler, zombie: zombieHandler, read_only: readOnlyHandler, }; const handler = foundationHandlers[foundationMethods]; if (!handler) { throw new Error(`Unsupported foundation methods: ${foundationMethods}`); } handler({ testCases: testCases, context, testCase, logger, ctx, }); }); } const loadParams = (config) => { const defaultParams = { maxConcurrent: 5, minTime: 100 }; if (!config || config.rateLimiter === undefined || config.rateLimiter === true) { return defaultParams; } if (config.rateLimiter === false) { return {}; } if (typeof config.rateLimiter === "object") { return config.rateLimiter; } }; const scheduleWithBottleneck = (api) => { return new Proxy(api, { get(target, propKey) { const origMethod = target[propKey]; if (typeof origMethod === "function" && propKey !== "rpc" && propKey !== "tx") { return (...args) => { if (!limiter) { throw new Error("Limiter not initialized"); } return limiter.schedule(() => origMethod.apply(target, args)); }; } return origMethod; }, }); }; //# sourceMappingURL=runnerContext.js.map