diff --git a/packages/core/package.json b/packages/core/package.json index 78563ecfe..27ac74973 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -33,7 +33,8 @@ "axios": "^1.4.0", "esbuild": "^0.19.5", "fs-extra": "^11.1.1", - "unzipper": "^0.10.14" + "unzipper": "^0.10.14", + "zod": "^3.22.4" }, "devDependencies": { "@types/archiver": "^5.3.3", diff --git a/packages/core/src/utils/getWalletSetupFuncHash.ts b/packages/core/src/utils/getWalletSetupFuncHash.ts index 521d42618..3a89ceab9 100644 --- a/packages/core/src/utils/getWalletSetupFuncHash.ts +++ b/packages/core/src/utils/getWalletSetupFuncHash.ts @@ -5,7 +5,7 @@ import esbuild from 'esbuild' export const WALLET_SETUP_FUNC_HASH_LENGTH = 10 // biome-ignore lint/suspicious/noExplicitAny: any type here is intentional -type AnyFunction = (...args: any) => any +type AnyFunction = (...args: any) => Promise export function getWalletSetupFuncHash(walletSetupFunc: AnyFunction) { // This transformation is necessary because a user could end up using a different execution engine than Playwright. diff --git a/packages/core/src/utils/importWalletSetupFile.ts b/packages/core/src/utils/importWalletSetupFile.ts new file mode 100644 index 000000000..498e86e65 --- /dev/null +++ b/packages/core/src/utils/importWalletSetupFile.ts @@ -0,0 +1,32 @@ +import { z } from 'zod' +import type { WalletSetupFunction } from '../defineWalletSetup' + +// TODO: Add hash length validation. +const WalletSetupModule = z.object({ + default: z.object({ + hash: z.string(), + fn: z.function().returns(z.promise(z.void())) + }) +}) + +export async function importWalletSetupFile(walletSetupFilePath: string) { + const walletSetupModule = await import(walletSetupFilePath) + + const result = WalletSetupModule.safeParse(walletSetupModule) + if (!result.success) { + throw new Error( + [ + `[ImportWalletSetupFile] Invalid wallet setup function at ${walletSetupFilePath}`, + 'Remember that all wallet setup files must export the wallet setup function as a default export!' + ].join('\n') + ) + } + + const { hash, fn } = result.data.default + + // TODO: Can we somehow validate this function type with Zod? + return { + hash, + fn: fn as WalletSetupFunction + } +} diff --git a/packages/core/test/utils/importWalletSetupFile.test.ts b/packages/core/test/utils/importWalletSetupFile.test.ts new file mode 100644 index 000000000..aac0eb2f0 --- /dev/null +++ b/packages/core/test/utils/importWalletSetupFile.test.ts @@ -0,0 +1,52 @@ +import { afterAll, describe, expect, it, vi } from 'vitest' +import { importWalletSetupFile } from '../../src/utils/importWalletSetupFile' + +vi.mock('./valid.setup.ts', async () => { + return { + default: { + hash: 'bca3f22838f317ed9e87', + fn: async () => { + return + } + } + } +}) + +vi.mock('./invalid.setup.ts', async () => { + return { + default: { + duck: 'Quack! 🦆' + } + } +}) + +describe('importWalletSetupFile', () => { + afterAll(() => { + vi.resetAllMocks() + }) + + // This error is from `await import`. + it('throws if the target file does not exist', async () => { + const nonExistentFilePath = './non-existent-file.ts' + await expect(importWalletSetupFile(nonExistentFilePath)).rejects.toThrowError( + `Failed to load url ${nonExistentFilePath}` + ) + }) + + it('throws if the target file is not a valid wallet setup file', async () => { + const invalidFilePath = './invalid.setup.ts' + await expect(importWalletSetupFile(invalidFilePath)).rejects.toThrowError( + `[ImportWalletSetupFile] Invalid wallet setup function at ${invalidFilePath}` + ) + }) + + it('returns the hash and function of a valid wallet setup file', async () => { + const validFilePath = './valid.setup.ts' + const result = await importWalletSetupFile(validFilePath) + + expect(result).toEqual({ + hash: 'bca3f22838f317ed9e87', + fn: expect.any(Function) + }) + }) +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90e333dd2..ee6fbd99e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -64,6 +64,9 @@ importers: unzipper: specifier: ^0.10.14 version: 0.10.14 + zod: + specifier: ^3.22.4 + version: 3.22.4 devDependencies: '@types/archiver': specifier: ^5.3.3