From 7b1ab3a2cfee8b62447089aaff5486dda6fcda38 Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Wed, 14 Aug 2024 04:11:22 -0400 Subject: [PATCH 1/9] mocha test plugin --- pnpm-lock.yaml | 79 +++++++++++++ v-next/example-project/hardhat.config.ts | 13 ++- v-next/example-project/package.json | 3 + .../example-project/test/mocha/mocha-test.ts | 8 ++ .../test/mocha/other-mocha-test.ts | 16 +++ .../test/{ => node}/example-test.ts | 0 .../test/{ => node}/other-example-test.js | 0 v-next/hardhat-errors/src/descriptors.ts | 7 ++ .../hardhat-mocha-test-runner/.eslintrc.cjs | 3 + v-next/hardhat-mocha-test-runner/.gitignore | 5 + .../hardhat-mocha-test-runner/.prettierignore | 3 + v-next/hardhat-mocha-test-runner/LICENSE | 9 ++ v-next/hardhat-mocha-test-runner/README.md | 3 + v-next/hardhat-mocha-test-runner/package.json | 67 +++++++++++ v-next/hardhat-mocha-test-runner/src/index.ts | 19 +++ .../src/task-action.ts | 109 ++++++++++++++++++ .../src/type-extensions.ts | 13 +++ .../hardhat-mocha-test-runner/test/index.ts | 0 .../hardhat-mocha-test-runner/tsconfig.json | 20 ++++ 19 files changed, 376 insertions(+), 1 deletion(-) create mode 100644 v-next/example-project/test/mocha/mocha-test.ts create mode 100644 v-next/example-project/test/mocha/other-mocha-test.ts rename v-next/example-project/test/{ => node}/example-test.ts (100%) rename v-next/example-project/test/{ => node}/other-example-test.js (100%) create mode 100644 v-next/hardhat-mocha-test-runner/.eslintrc.cjs create mode 100644 v-next/hardhat-mocha-test-runner/.gitignore create mode 100644 v-next/hardhat-mocha-test-runner/.prettierignore create mode 100644 v-next/hardhat-mocha-test-runner/LICENSE create mode 100644 v-next/hardhat-mocha-test-runner/README.md create mode 100644 v-next/hardhat-mocha-test-runner/package.json create mode 100644 v-next/hardhat-mocha-test-runner/src/index.ts create mode 100644 v-next/hardhat-mocha-test-runner/src/task-action.ts create mode 100644 v-next/hardhat-mocha-test-runner/src/type-extensions.ts create mode 100644 v-next/hardhat-mocha-test-runner/test/index.ts create mode 100644 v-next/hardhat-mocha-test-runner/tsconfig.json diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9862e400f2..90f9b94428 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1514,12 +1514,21 @@ importers: '@ignored/hardhat-vnext': specifier: workspace:^3.0.0-next.3 version: link:../hardhat + '@ignored/hardhat-vnext-mocha-test-runner': + specifier: workspace:^3.0.0-next.2 + version: link:../hardhat-mocha-test-runner '@ignored/hardhat-vnext-node-test-runner': specifier: workspace:^3.0.0-next.2 version: link:../hardhat-node-test-runner + '@types/mocha': + specifier: '>=9.1.0' + version: 10.0.7 '@types/node': specifier: ^20.14.9 version: 20.14.14 + mocha: + specifier: ^10.0.0 + version: 10.7.0 prettier: specifier: 3.2.5 version: 3.2.5 @@ -1812,6 +1821,76 @@ importers: specifier: 7.7.1 version: 7.7.1(eslint@8.57.0)(typescript@5.5.4) + v-next/hardhat-mocha-test-runner: + dependencies: + '@ignored/hardhat-vnext-core': + specifier: workspace:^3.0.0-next.2 + version: link:../core + '@ignored/hardhat-vnext-errors': + specifier: workspace:^3.0.0-next.2 + version: link:../hardhat-errors + '@ignored/hardhat-vnext-utils': + specifier: workspace:^3.0.0-next.2 + version: link:../hardhat-utils + mocha: + specifier: ^10.0.0 + version: 10.7.0 + tsx: + specifier: ^4.11.0 + version: 4.16.5 + devDependencies: + '@eslint-community/eslint-plugin-eslint-comments': + specifier: ^4.3.0 + version: 4.3.0(eslint@8.57.0) + '@ignored/hardhat-vnext-node-test-reporter': + specifier: workspace:^3.0.0-next.2 + version: link:../hardhat-node-test-reporter + '@nomicfoundation/hardhat-test-utils': + specifier: workspace:^ + version: link:../hardhat-test-utils + '@types/mocha': + specifier: '>=9.1.0' + version: 10.0.7 + '@types/node': + specifier: ^20.14.9 + version: 20.14.14 + '@typescript-eslint/eslint-plugin': + specifier: ^7.7.1 + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/parser': + specifier: ^7.7.1 + version: 7.18.0(eslint@8.57.0)(typescript@5.5.4) + eslint: + specifier: 8.57.0 + version: 8.57.0 + eslint-config-prettier: + specifier: 9.1.0 + version: 9.1.0(eslint@8.57.0) + eslint-import-resolver-typescript: + specifier: ^3.6.1 + version: 3.6.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-plugin-import: + specifier: 2.29.1 + version: 2.29.1(@typescript-eslint/parser@7.18.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) + eslint-plugin-no-only-tests: + specifier: 3.1.0 + version: 3.1.0 + expect-type: + specifier: ^0.19.0 + version: 0.19.0 + prettier: + specifier: 3.2.5 + version: 3.2.5 + rimraf: + specifier: ^5.0.5 + version: 5.0.10 + typescript: + specifier: ~5.5.0 + version: 5.5.4 + typescript-eslint: + specifier: 7.7.1 + version: 7.7.1(eslint@8.57.0)(typescript@5.5.4) + v-next/hardhat-node-test-reporter: dependencies: '@actions/core': diff --git a/v-next/example-project/hardhat.config.ts b/v-next/example-project/hardhat.config.ts index 81372d83a7..f2e22e1da6 100644 --- a/v-next/example-project/hardhat.config.ts +++ b/v-next/example-project/hardhat.config.ts @@ -8,6 +8,7 @@ import { HardhatUserConfig, } from "@ignored/hardhat-vnext/config"; import HardhatNodeTestRunner from "@ignored/hardhat-vnext-node-test-runner"; +import HardhatMochaTestRunner from "@ignored/hardhat-vnext-mocha-test-runner"; const exampleEmptyTask = emptyTask("empty", "An example empty task").build(); @@ -102,8 +103,18 @@ const config: HardhatUserConfig = { exampleEmptySubtask, greeting, ], - plugins: [pluginExample, HardhatNodeTestRunner], + plugins: [ + pluginExample, + HardhatMochaTestRunner, + // if testing node plugin, use the following line instead + // HardhatNodeTestRunner, + ], privateKey: configVariable("privateKey"), + paths: { + tests: "test/mocha", + // if testing node plugin, use the following line instead + // tests: "test/node", + }, }; export default config; diff --git a/v-next/example-project/package.json b/v-next/example-project/package.json index b4a3fe3fa7..4598e79d90 100644 --- a/v-next/example-project/package.json +++ b/v-next/example-project/package.json @@ -22,8 +22,11 @@ }, "devDependencies": { "@ignored/hardhat-vnext": "workspace:^3.0.0-next.3", + "@ignored/hardhat-vnext-mocha-test-runner": "workspace:^3.0.0-next.2", "@ignored/hardhat-vnext-node-test-runner": "workspace:^3.0.0-next.2", + "@types/mocha": ">=9.1.0", "@types/node": "^20.14.9", + "mocha": "^10.0.0", "prettier": "3.2.5", "typescript": "~5.5.0" }, diff --git a/v-next/example-project/test/mocha/mocha-test.ts b/v-next/example-project/test/mocha/mocha-test.ts new file mode 100644 index 0000000000..e9e35fd3d0 --- /dev/null +++ b/v-next/example-project/test/mocha/mocha-test.ts @@ -0,0 +1,8 @@ +import assert from "node:assert/strict"; +import { describe, it } from "mocha"; + +describe("Mocha test", () => { + it("should work", () => { + assert.equal(1 + 1, 2); + }); +}); diff --git a/v-next/example-project/test/mocha/other-mocha-test.ts b/v-next/example-project/test/mocha/other-mocha-test.ts new file mode 100644 index 0000000000..263de17a17 --- /dev/null +++ b/v-next/example-project/test/mocha/other-mocha-test.ts @@ -0,0 +1,16 @@ +import assert from "node:assert/strict"; +import { describe, it } from "mocha"; + +// import hre from "@ignored/hardhat-vnext"; + +// describe("Other mocha test", () => { +// it("should have the example task", () => { +// assert.ok(hre.tasks.getTask("example")); +// }); +// }); + +describe("Other mocha test", () => { + it("should have the example task", () => { + assert.ok(true); + }); +}); diff --git a/v-next/example-project/test/example-test.ts b/v-next/example-project/test/node/example-test.ts similarity index 100% rename from v-next/example-project/test/example-test.ts rename to v-next/example-project/test/node/example-test.ts diff --git a/v-next/example-project/test/other-example-test.js b/v-next/example-project/test/node/other-example-test.js similarity index 100% rename from v-next/example-project/test/other-example-test.js rename to v-next/example-project/test/node/other-example-test.js diff --git a/v-next/hardhat-errors/src/descriptors.ts b/v-next/hardhat-errors/src/descriptors.ts index 23068c5426..eafc52d97e 100644 --- a/v-next/hardhat-errors/src/descriptors.ts +++ b/v-next/hardhat-errors/src/descriptors.ts @@ -473,6 +473,13 @@ Please double check your script's path.`, Please check Hardhat's output for more details.`, }, + TEST_TASK_ESM_TESTS_RUN_TWICE: { + number: 602, + messageTemplate: `Your project uses ESM and you've programmatically run your tests twice. This is not supported yet.`, + websiteTitle: "Running tests twice in an ESM project", + websiteDescription: + 'You have run your tests twice programmatically and your project is an ESM project (you have `"type": "module"` in your `package.json`, or some of your files have the `.mjs` extension). This is not supported by Mocha yet.', + }, }, NETWORK: { INVALID_URL: { diff --git a/v-next/hardhat-mocha-test-runner/.eslintrc.cjs b/v-next/hardhat-mocha-test-runner/.eslintrc.cjs new file mode 100644 index 0000000000..939317ab1c --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/.eslintrc.cjs @@ -0,0 +1,3 @@ +const { createConfig } = require("../../config-v-next/eslint.cjs"); + +module.exports = createConfig(__filename); diff --git a/v-next/hardhat-mocha-test-runner/.gitignore b/v-next/hardhat-mocha-test-runner/.gitignore new file mode 100644 index 0000000000..6aa5402c62 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/.gitignore @@ -0,0 +1,5 @@ +# Node modules +/node_modules + +# Compilation output +/dist diff --git a/v-next/hardhat-mocha-test-runner/.prettierignore b/v-next/hardhat-mocha-test-runner/.prettierignore new file mode 100644 index 0000000000..3760c88cd5 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/.prettierignore @@ -0,0 +1,3 @@ +/node_modules +/dist +CHANGELOG.md diff --git a/v-next/hardhat-mocha-test-runner/LICENSE b/v-next/hardhat-mocha-test-runner/LICENSE new file mode 100644 index 0000000000..0781b4a819 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2024 Nomic Foundation + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/v-next/hardhat-mocha-test-runner/README.md b/v-next/hardhat-mocha-test-runner/README.md new file mode 100644 index 0000000000..fd9c36bf77 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/README.md @@ -0,0 +1,3 @@ +# Hardhat's `mocha` test runner + +This package includes Hardhat's `mocha` test runner. diff --git a/v-next/hardhat-mocha-test-runner/package.json b/v-next/hardhat-mocha-test-runner/package.json new file mode 100644 index 0000000000..1de71aaca8 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/package.json @@ -0,0 +1,67 @@ +{ + "name": "@ignored/hardhat-vnext-mocha-test-runner", + "version": "3.0.0-next.2", + "description": "A mocha test runner", + "homepage": "https://github.com/nomicfoundation/hardhat/tree/v-next/v-next/hardhat-mocha-test-runner", + "repository": { + "type": "git", + "url": "https://github.com/NomicFoundation/hardhat", + "directory": "v-next/hardhat-mocha-test-runner" + }, + "author": "Nomic Foundation", + "license": "MIT", + "type": "module", + "exports": { + ".": "./dist/src/index.js" + }, + "keywords": [ + "ethereum", + "smart-contracts", + "hardhat" + ], + "scripts": { + "lint": "pnpm prettier --check && pnpm eslint", + "lint:fix": "pnpm prettier --write && pnpm eslint --fix", + "eslint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"", + "prettier": "prettier \"**/*.{ts,js,md,json}\"", + "test": "node --import tsx/esm --test --test-reporter=@ignored/hardhat-vnext-node-test-reporter \"test/*.ts\" \"test/!(fixture-projects|helpers)/**/*.ts\"", + "test:only": "node --import tsx/esm --test --test-only --test-reporter=@ignored/hardhat-vnext-node-test-reporter \"test/*.ts\" \"test/!(fixture-projects|helpers)/**/*.ts\"", + "pretest": "pnpm build", + "build": "tsc --build .", + "prepublishOnly": "pnpm build", + "clean": "rimraf dist" + }, + "files": [ + "dist/src/", + "src/", + "CHANGELOG.md", + "LICENSE", + "README.md" + ], + "devDependencies": { + "@eslint-community/eslint-plugin-eslint-comments": "^4.3.0", + "@ignored/hardhat-vnext-node-test-reporter": "workspace:^3.0.0-next.2", + "@nomicfoundation/hardhat-test-utils": "workspace:^", + "@types/mocha": ">=9.1.0", + "@types/node": "^20.14.9", + "@typescript-eslint/eslint-plugin": "^7.7.1", + "@typescript-eslint/parser": "^7.7.1", + "eslint": "8.57.0", + "eslint-config-prettier": "9.1.0", + "eslint-import-resolver-typescript": "^3.6.1", + "eslint-plugin-import": "2.29.1", + "eslint-plugin-no-only-tests": "3.1.0", + "expect-type": "^0.19.0", + "prettier": "3.2.5", + "rimraf": "^5.0.5", + "typescript": "~5.5.0", + "typescript-eslint": "7.7.1" + }, + "dependencies": { + "@ignored/hardhat-vnext-core": "workspace:^3.0.0-next.2", + "@ignored/hardhat-vnext-errors": "workspace:^3.0.0-next.2", + "@ignored/hardhat-vnext-utils": "workspace:^3.0.0-next.2", + "mocha": "^10.0.0", + "tsx": "^4.11.0" + } +} diff --git a/v-next/hardhat-mocha-test-runner/src/index.ts b/v-next/hardhat-mocha-test-runner/src/index.ts new file mode 100644 index 0000000000..0b8bcdbb90 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/src/index.ts @@ -0,0 +1,19 @@ +import type { HardhatPlugin } from "@ignored/hardhat-vnext-core/types/plugins"; + +import { task } from "@ignored/hardhat-vnext-core/config"; + +const hardhatPlugin: HardhatPlugin = { + id: "test", + tasks: [ + task("test", "Runs tests using the Mocha test runner") + .addVariadicArgument({ + name: "testFiles", + description: "An optional list of files to test", + defaultValue: [], + }) + .setAction(import.meta.resolve("./task-action.js")) + .build(), + ], +}; + +export default hardhatPlugin; diff --git a/v-next/hardhat-mocha-test-runner/src/task-action.ts b/v-next/hardhat-mocha-test-runner/src/task-action.ts new file mode 100644 index 0000000000..86d877bfbf --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/src/task-action.ts @@ -0,0 +1,109 @@ +import type { HardhatConfig } from "@ignored/hardhat-vnext-core/types/config"; +import type { NewTaskActionFunction } from "@ignored/hardhat-vnext-core/types/tasks"; +import type { MochaOptions } from "mocha"; + +import { resolve as pathResolve } from "node:path"; + +import { HardhatError } from "@ignored/hardhat-vnext-errors"; +import { findUp, getAllFilesMatching } from "@ignored/hardhat-vnext-utils/fs"; +import { readClosestPackageJson } from "@ignored/hardhat-vnext-utils/package"; + +interface TestActionArguments { + testFiles: string[]; +} + +function isTypescriptFile(path: string): boolean { + return /\.(ts|cts|mts)$/i.test(path); +} + +function isJavascriptFile(path: string): boolean { + return /\.(js|cjs|mjs)$/i.test(path); +} + +async function getTestFiles( + testFiles: string[], + config: HardhatConfig, +): Promise { + if (testFiles.length !== 0) { + const testFilesAbsolutePaths = testFiles.map((x) => + pathResolve(process.cwd(), x), + ); + + return testFilesAbsolutePaths; + } + + return getAllFilesMatching( + config.paths.tests, + (f) => isJavascriptFile(f) || isTypescriptFile(f), + ); +} + +async function hasTypescriptConfig(): Promise { + const hhConfig = await findUp("hardhat.config.ts"); + + return hhConfig !== undefined; +} + +let testsAlreadyRun = false; +const testWithHardhat: NewTaskActionFunction = async ( + { testFiles }, + hre, +) => { + const files = await getTestFiles(testFiles, hre.config); + + // the second check is needed for the case of a user having a hardhat.config.ts file + // but all their test files are js files. probably an edge case, but we should handle it + if (files.some((f) => isTypescriptFile(f)) || (await hasTypescriptConfig())) { + try { + import.meta.resolve("typescript"); + } catch { + throw new HardhatError( + HardhatError.ERRORS.GENERAL.TYPESCRIPT_NOT_INSTALLED, + ); + } + + try { + import.meta.resolve("tsx"); + } catch { + throw new HardhatError(HardhatError.ERRORS.GENERAL.TSX_NOT_INSTALLED); + } + + process.env.NODE_OPTIONS = "--import tsx"; + } + + const { default: Mocha } = await import("mocha"); + + const mochaConfig: MochaOptions = { ...hre.config.mocha }; + + const mocha = new Mocha(mochaConfig); + + files.forEach((file) => mocha.addFile(file)); + + // if the project is of type "module" or if there's some ESM test file, + // we call loadFilesAsync to enable Mocha's ESM support + const projectPackageJson = await readClosestPackageJson(import.meta.url); + const isTypeModule = projectPackageJson.type === "module"; + const hasEsmTest = files.some((file) => file.endsWith(".mjs")); + + if (isTypeModule || hasEsmTest) { + // Because of the way the ESM cache works, loadFilesAsync doesn't work + // correctly if used twice within the same process, so we throw an error + // in that case + if (testsAlreadyRun) { + throw new HardhatError( + HardhatError.ERRORS.BUILTIN_TASKS.TEST_TASK_ESM_TESTS_RUN_TWICE, + ); + } + testsAlreadyRun = true; + + // This instructs Mocha to use the more verbose file loading infrastructure + // which supports both ESM and CJS + await mocha.loadFilesAsync(); + } + + await new Promise((resolve) => { + mocha.run(resolve); + }); +}; + +export default testWithHardhat; diff --git a/v-next/hardhat-mocha-test-runner/src/type-extensions.ts b/v-next/hardhat-mocha-test-runner/src/type-extensions.ts new file mode 100644 index 0000000000..60b8472459 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/src/type-extensions.ts @@ -0,0 +1,13 @@ +import "@ignored/hardhat-vnext-core/types/config"; + +import type { MochaOptions } from "mocha"; + +declare module "@ignored/hardhat-vnext-core/types/config" { + export interface HardhatUserConfig { + mocha?: MochaOptions; + } + + export interface HardhatConfig { + mocha: MochaOptions; + } +} diff --git a/v-next/hardhat-mocha-test-runner/test/index.ts b/v-next/hardhat-mocha-test-runner/test/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/v-next/hardhat-mocha-test-runner/tsconfig.json b/v-next/hardhat-mocha-test-runner/tsconfig.json new file mode 100644 index 0000000000..f75a3d4d50 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../config-v-next/tsconfig.json", + "references": [ + { + "path": "../core" + }, + { + "path": "../hardhat-errors" + }, + { + "path": "../hardhat-node-test-reporter" + }, + { + "path": "../hardhat-test-utils" + }, + { + "path": "../hardhat-utils" + } + ] +} From fbe02dd322dae5127b724309eb2e8738d5200f65 Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Mon, 19 Aug 2024 03:51:51 -0400 Subject: [PATCH 2/9] misc fixes for PR feedback --- pnpm-lock.yaml | 4 +- .../test/mocha/other-mocha-test.ts | 10 +-- v-next/hardhat-mocha-test-runner/package.json | 2 +- v-next/hardhat-mocha-test-runner/src/index.ts | 13 ++- .../src/task-action.ts | 87 ++++++++----------- .../src/type-extensions.ts | 4 +- .../hardhat-mocha-test-runner/tsconfig.json | 2 +- 7 files changed, 55 insertions(+), 67 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 90f9b94428..b3e373113d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1823,9 +1823,9 @@ importers: v-next/hardhat-mocha-test-runner: dependencies: - '@ignored/hardhat-vnext-core': + '@ignored/hardhat-vnext': specifier: workspace:^3.0.0-next.2 - version: link:../core + version: link:../hardhat '@ignored/hardhat-vnext-errors': specifier: workspace:^3.0.0-next.2 version: link:../hardhat-errors diff --git a/v-next/example-project/test/mocha/other-mocha-test.ts b/v-next/example-project/test/mocha/other-mocha-test.ts index 263de17a17..fe6bb98d3b 100644 --- a/v-next/example-project/test/mocha/other-mocha-test.ts +++ b/v-next/example-project/test/mocha/other-mocha-test.ts @@ -1,16 +1,10 @@ import assert from "node:assert/strict"; import { describe, it } from "mocha"; -// import hre from "@ignored/hardhat-vnext"; - -// describe("Other mocha test", () => { -// it("should have the example task", () => { -// assert.ok(hre.tasks.getTask("example")); -// }); -// }); +import hre from "@ignored/hardhat-vnext"; describe("Other mocha test", () => { it("should have the example task", () => { - assert.ok(true); + assert.ok(hre.tasks.getTask("example")); }); }); diff --git a/v-next/hardhat-mocha-test-runner/package.json b/v-next/hardhat-mocha-test-runner/package.json index 1de71aaca8..95ac57b1e8 100644 --- a/v-next/hardhat-mocha-test-runner/package.json +++ b/v-next/hardhat-mocha-test-runner/package.json @@ -58,7 +58,7 @@ "typescript-eslint": "7.7.1" }, "dependencies": { - "@ignored/hardhat-vnext-core": "workspace:^3.0.0-next.2", + "@ignored/hardhat-vnext": "workspace:^3.0.0-next.2", "@ignored/hardhat-vnext-errors": "workspace:^3.0.0-next.2", "@ignored/hardhat-vnext-utils": "workspace:^3.0.0-next.2", "mocha": "^10.0.0", diff --git a/v-next/hardhat-mocha-test-runner/src/index.ts b/v-next/hardhat-mocha-test-runner/src/index.ts index 0b8bcdbb90..088c3b1235 100644 --- a/v-next/hardhat-mocha-test-runner/src/index.ts +++ b/v-next/hardhat-mocha-test-runner/src/index.ts @@ -1,6 +1,6 @@ -import type { HardhatPlugin } from "@ignored/hardhat-vnext-core/types/plugins"; +import type { HardhatPlugin } from "@ignored/hardhat-vnext/types/plugins"; -import { task } from "@ignored/hardhat-vnext-core/config"; +import { task } from "@ignored/hardhat-vnext/config"; const hardhatPlugin: HardhatPlugin = { id: "test", @@ -11,6 +11,15 @@ const hardhatPlugin: HardhatPlugin = { description: "An optional list of files to test", defaultValue: [], }) + .addFlag({ + name: "bail", + description: "Stop running tests after the first test failure", + }) + .addOption({ + name: "grep", + description: "Only run tests matching the given string or regexp", + defaultValue: "", + }) .setAction(import.meta.resolve("./task-action.js")) .build(), ], diff --git a/v-next/hardhat-mocha-test-runner/src/task-action.ts b/v-next/hardhat-mocha-test-runner/src/task-action.ts index 86d877bfbf..f9c1a85c60 100644 --- a/v-next/hardhat-mocha-test-runner/src/task-action.ts +++ b/v-next/hardhat-mocha-test-runner/src/task-action.ts @@ -1,15 +1,17 @@ -import type { HardhatConfig } from "@ignored/hardhat-vnext-core/types/config"; -import type { NewTaskActionFunction } from "@ignored/hardhat-vnext-core/types/tasks"; +import type { HardhatConfig } from "@ignored/hardhat-vnext/types/config"; +import type { NewTaskActionFunction } from "@ignored/hardhat-vnext/types/tasks"; import type { MochaOptions } from "mocha"; import { resolve as pathResolve } from "node:path"; +import { fileURLToPath } from "node:url"; import { HardhatError } from "@ignored/hardhat-vnext-errors"; -import { findUp, getAllFilesMatching } from "@ignored/hardhat-vnext-utils/fs"; -import { readClosestPackageJson } from "@ignored/hardhat-vnext-utils/package"; +import { getAllFilesMatching } from "@ignored/hardhat-vnext-utils/fs"; interface TestActionArguments { testFiles: string[]; + bail: boolean; + grep: string; } function isTypescriptFile(path: string): boolean { @@ -38,72 +40,55 @@ async function getTestFiles( ); } -async function hasTypescriptConfig(): Promise { - const hhConfig = await findUp("hardhat.config.ts"); - - return hhConfig !== undefined; -} - let testsAlreadyRun = false; const testWithHardhat: NewTaskActionFunction = async ( - { testFiles }, + { testFiles, bail, grep }, hre, ) => { const files = await getTestFiles(testFiles, hre.config); - // the second check is needed for the case of a user having a hardhat.config.ts file - // but all their test files are js files. probably an edge case, but we should handle it - if (files.some((f) => isTypescriptFile(f)) || (await hasTypescriptConfig())) { - try { - import.meta.resolve("typescript"); - } catch { - throw new HardhatError( - HardhatError.ERRORS.GENERAL.TYPESCRIPT_NOT_INSTALLED, - ); - } - - try { - import.meta.resolve("tsx"); - } catch { - throw new HardhatError(HardhatError.ERRORS.GENERAL.TSX_NOT_INSTALLED); - } - - process.env.NODE_OPTIONS = "--import tsx"; - } + const tsx = fileURLToPath(import.meta.resolve("tsx/esm")); + process.env.NODE_OPTIONS = `--import ${tsx}`; const { default: Mocha } = await import("mocha"); const mochaConfig: MochaOptions = { ...hre.config.mocha }; + if (grep !== "") { + mochaConfig.grep = grep; + } + + if (bail) { + mochaConfig.bail = true; + } + const mocha = new Mocha(mochaConfig); files.forEach((file) => mocha.addFile(file)); - // if the project is of type "module" or if there's some ESM test file, - // we call loadFilesAsync to enable Mocha's ESM support - const projectPackageJson = await readClosestPackageJson(import.meta.url); - const isTypeModule = projectPackageJson.type === "module"; - const hasEsmTest = files.some((file) => file.endsWith(".mjs")); - - if (isTypeModule || hasEsmTest) { - // Because of the way the ESM cache works, loadFilesAsync doesn't work - // correctly if used twice within the same process, so we throw an error - // in that case - if (testsAlreadyRun) { - throw new HardhatError( - HardhatError.ERRORS.BUILTIN_TASKS.TEST_TASK_ESM_TESTS_RUN_TWICE, - ); - } - testsAlreadyRun = true; - - // This instructs Mocha to use the more verbose file loading infrastructure - // which supports both ESM and CJS - await mocha.loadFilesAsync(); + // Because of the way the ESM cache works, loadFilesAsync doesn't work + // correctly if used twice within the same process, so we throw an error + // in that case + if (testsAlreadyRun) { + throw new HardhatError( + HardhatError.ERRORS.BUILTIN_TASKS.TEST_TASK_ESM_TESTS_RUN_TWICE, + ); } + testsAlreadyRun = true; + + // This instructs Mocha to use the more verbose file loading infrastructure + // which supports both ESM and CJS + await mocha.loadFilesAsync(); - await new Promise((resolve) => { + const testFailures = await new Promise((resolve) => { mocha.run(resolve); }); + + if (testFailures > 0) { + process.exitCode = testFailures; + } + + return testFailures; }; export default testWithHardhat; diff --git a/v-next/hardhat-mocha-test-runner/src/type-extensions.ts b/v-next/hardhat-mocha-test-runner/src/type-extensions.ts index 60b8472459..097686e986 100644 --- a/v-next/hardhat-mocha-test-runner/src/type-extensions.ts +++ b/v-next/hardhat-mocha-test-runner/src/type-extensions.ts @@ -1,8 +1,8 @@ -import "@ignored/hardhat-vnext-core/types/config"; +import "@ignored/hardhat-vnext/types/config"; import type { MochaOptions } from "mocha"; -declare module "@ignored/hardhat-vnext-core/types/config" { +declare module "@ignored/hardhat-vnext/types/config" { export interface HardhatUserConfig { mocha?: MochaOptions; } diff --git a/v-next/hardhat-mocha-test-runner/tsconfig.json b/v-next/hardhat-mocha-test-runner/tsconfig.json index f75a3d4d50..47b339df71 100644 --- a/v-next/hardhat-mocha-test-runner/tsconfig.json +++ b/v-next/hardhat-mocha-test-runner/tsconfig.json @@ -2,7 +2,7 @@ "extends": "../../config-v-next/tsconfig.json", "references": [ { - "path": "../core" + "path": "../hardhat" }, { "path": "../hardhat-errors" From 361a596a3522aad9b64a8a274ef02f8f22b2ec12 Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Wed, 21 Aug 2024 02:20:49 -0400 Subject: [PATCH 3/9] config resolution hook, unit test, and type fixes --- pnpm-lock.yaml | 6 ++ v-next/example-project/hardhat.config.ts | 3 +- v-next/example-project/tsconfig.json | 6 ++ v-next/hardhat-mocha-test-runner/package.json | 4 +- .../src/hookHandlers/config.ts | 79 +++++++++++++++++++ v-next/hardhat-mocha-test-runner/src/index.ts | 5 ++ .../test-project/hardhat.config.ts | 9 +++ .../test-project/test/test.ts | 15 ++++ .../hardhat-mocha-test-runner/test/index.ts | 16 ++++ .../src/fixture-projects.ts | 53 +++++++++++++ v-next/hardhat-test-utils/src/index.ts | 1 + 11 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts create mode 100644 v-next/hardhat-mocha-test-runner/test/fixture-projects/test-project/hardhat.config.ts create mode 100644 v-next/hardhat-mocha-test-runner/test/fixture-projects/test-project/test/test.ts create mode 100644 v-next/hardhat-test-utils/src/fixture-projects.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b3e373113d..7dda362330 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1832,12 +1832,18 @@ importers: '@ignored/hardhat-vnext-utils': specifier: workspace:^3.0.0-next.2 version: link:../hardhat-utils + '@ignored/hardhat-vnext-zod-utils': + specifier: workspace:^3.0.0-next.2 + version: link:../hardhat-zod-utils mocha: specifier: ^10.0.0 version: 10.7.0 tsx: specifier: ^4.11.0 version: 4.16.5 + zod: + specifier: ^3.23.8 + version: 3.23.8 devDependencies: '@eslint-community/eslint-plugin-eslint-comments': specifier: ^4.3.0 diff --git a/v-next/example-project/hardhat.config.ts b/v-next/example-project/hardhat.config.ts index f2e22e1da6..d078b22450 100644 --- a/v-next/example-project/hardhat.config.ts +++ b/v-next/example-project/hardhat.config.ts @@ -1,3 +1,5 @@ +import type { HardhatUserConfig } from "@ignored/hardhat-vnext/types/config"; + import { HardhatPluginError } from "@ignored/hardhat-vnext/plugins"; import { @@ -5,7 +7,6 @@ import { emptyTask, configVariable, globalOption, - HardhatUserConfig, } from "@ignored/hardhat-vnext/config"; import HardhatNodeTestRunner from "@ignored/hardhat-vnext-node-test-runner"; import HardhatMochaTestRunner from "@ignored/hardhat-vnext-mocha-test-runner"; diff --git a/v-next/example-project/tsconfig.json b/v-next/example-project/tsconfig.json index 5372c25bb5..62ca286dac 100644 --- a/v-next/example-project/tsconfig.json +++ b/v-next/example-project/tsconfig.json @@ -3,6 +3,12 @@ "references": [ { "path": "../hardhat" + }, + { + "path": "../hardhat-node-test-runner" + }, + { + "path": "../hardhat-mocha-test-runner" } ] } diff --git a/v-next/hardhat-mocha-test-runner/package.json b/v-next/hardhat-mocha-test-runner/package.json index 95ac57b1e8..6a4ca9b05f 100644 --- a/v-next/hardhat-mocha-test-runner/package.json +++ b/v-next/hardhat-mocha-test-runner/package.json @@ -61,7 +61,9 @@ "@ignored/hardhat-vnext": "workspace:^3.0.0-next.2", "@ignored/hardhat-vnext-errors": "workspace:^3.0.0-next.2", "@ignored/hardhat-vnext-utils": "workspace:^3.0.0-next.2", + "@ignored/hardhat-vnext-zod-utils": "workspace:^3.0.0-next.2", "mocha": "^10.0.0", - "tsx": "^4.11.0" + "tsx": "^4.11.0", + "zod": "^3.23.8" } } diff --git a/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts b/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts new file mode 100644 index 0000000000..225e84097a --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts @@ -0,0 +1,79 @@ +import type { ConfigHooks } from "@ignored/hardhat-vnext/types/hooks"; + +import { validateUserConfigZodType } from "@ignored/hardhat-vnext-zod-utils"; +import { z } from "zod"; + +const mochaConfigType = z.object({ + allowUncaught: z.boolean().optional(), + asyncOnly: z.boolean().optional(), + bail: z.boolean().optional(), + checkLeaks: z.boolean().optional(), + color: z.boolean().optional(), + delay: z.boolean().optional(), + diff: z.boolean().optional(), + dryRun: z.boolean().optional(), + failZero: z.boolean().optional(), + fgrep: z.string().optional(), + forbidOnly: z.boolean().optional(), + forbidPending: z.boolean().optional(), + fullTrace: z.boolean().optional(), + globals: z.array(z.string()).optional(), + grep: z.string().optional(), + growl: z.boolean().optional(), + inlineDiffs: z.boolean().optional(), + invert: z.boolean().optional(), + noHighlighting: z.boolean().optional(), + reporter: z.string().optional(), + reporterOptions: z.any().optional(), + retries: z.number().optional(), + slow: z.number().optional(), + timeout: z.union([z.number(), z.string()]).optional(), + ui: z + .union([ + z.literal("bdd"), + z.literal("tdd"), + z.literal("qunit"), + z.literal("exports"), + ]) + .optional(), + parallel: z.boolean().optional(), + jobs: z.number().optional(), + rootHooks: z + .object({ + afterAll: z.union([z.function(), z.array(z.function())]).optional(), + beforeAll: z.union([z.function(), z.array(z.function())]).optional(), + afterEach: z.union([z.function(), z.array(z.function())]).optional(), + beforeEach: z.union([z.function(), z.array(z.function())]).optional(), + }) + .optional(), + require: z.array(z.string()).optional(), + isWorker: z.boolean().optional(), +}); + +export default async (): Promise> => { + const handlers: Partial = { + validateUserConfig: async (userConfig) => { + return validateUserConfigZodType(userConfig, mochaConfigType); + }, + resolveUserConfig: async ( + userConfig, + resolveConfigurationVariable, + next, + ) => { + const resolvedConfig = await next( + userConfig, + resolveConfigurationVariable, + ); + + return { + ...resolvedConfig, + mocha: { + ...resolvedConfig.mocha, + ...userConfig.mocha, + }, + }; + }, + }; + + return handlers; +}; diff --git a/v-next/hardhat-mocha-test-runner/src/index.ts b/v-next/hardhat-mocha-test-runner/src/index.ts index 088c3b1235..5ca74c73f2 100644 --- a/v-next/hardhat-mocha-test-runner/src/index.ts +++ b/v-next/hardhat-mocha-test-runner/src/index.ts @@ -2,6 +2,8 @@ import type { HardhatPlugin } from "@ignored/hardhat-vnext/types/plugins"; import { task } from "@ignored/hardhat-vnext/config"; +import "./type-extensions.js"; + const hardhatPlugin: HardhatPlugin = { id: "test", tasks: [ @@ -23,6 +25,9 @@ const hardhatPlugin: HardhatPlugin = { .setAction(import.meta.resolve("./task-action.js")) .build(), ], + hookHandlers: { + config: import.meta.resolve("./hookHandlers/config.js"), + }, }; export default hardhatPlugin; diff --git a/v-next/hardhat-mocha-test-runner/test/fixture-projects/test-project/hardhat.config.ts b/v-next/hardhat-mocha-test-runner/test/fixture-projects/test-project/hardhat.config.ts new file mode 100644 index 0000000000..e59936162c --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/test/fixture-projects/test-project/hardhat.config.ts @@ -0,0 +1,9 @@ +import type { HardhatUserConfig } from "@ignored/hardhat-vnext/config"; + +import HardhatMochaPlugin from "../../../src/index.js"; + +const config: HardhatUserConfig = { + plugins: [HardhatMochaPlugin], +}; + +export default config; diff --git a/v-next/hardhat-mocha-test-runner/test/fixture-projects/test-project/test/test.ts b/v-next/hardhat-mocha-test-runner/test/fixture-projects/test-project/test/test.ts new file mode 100644 index 0000000000..05185a516f --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/test/fixture-projects/test-project/test/test.ts @@ -0,0 +1,15 @@ +import assert from "node:assert/strict"; + +import hre from "@ignored/hardhat-vnext"; +import { describe, it } from "mocha"; + +describe("Mocha test", () => { + it("should work", () => { + assert.equal(1 + 1, 2); + }); + + it("should have the test task", () => { + // throws if the task doesn't exist + hre.tasks.getTask("test"); + }); +}); diff --git a/v-next/hardhat-mocha-test-runner/test/index.ts b/v-next/hardhat-mocha-test-runner/test/index.ts index e69de29bb2..1011c31768 100644 --- a/v-next/hardhat-mocha-test-runner/test/index.ts +++ b/v-next/hardhat-mocha-test-runner/test/index.ts @@ -0,0 +1,16 @@ +import assert from "node:assert/strict"; +import { describe, it } from "node:test"; + +import { useFixtureProject } from "@nomicfoundation/hardhat-test-utils"; + +describe("Hardhat Mocha plugin", () => { + useFixtureProject("test-project"); + + it("should work", async () => { + const hre = await import("@ignored/hardhat-vnext"); + + const result = await hre.tasks.getTask("test").run({}); + + assert.equal(result, 0); + }); +}); diff --git a/v-next/hardhat-test-utils/src/fixture-projects.ts b/v-next/hardhat-test-utils/src/fixture-projects.ts new file mode 100644 index 0000000000..afb70e3ffe --- /dev/null +++ b/v-next/hardhat-test-utils/src/fixture-projects.ts @@ -0,0 +1,53 @@ +import path from "node:path"; +import { before, after } from "node:test"; + +import { exists, getRealPath } from "@ignored/hardhat-vnext-utils/fs"; + +/** + * This helper adds node:test hooks to run the tests inside one of the projects + * from test/fixture-projects. Assumes you are running from the root of the project. + * + * @param projectName The base name of the folder with the project to use. + * @param changeDirTo If provided, the working directory will be changed to this. Must be a child of the project folder. + */ +export function useFixtureProject( + projectName: string, + changeDirTo?: string, +): void { + let projectPath: string; + let prevWorkingDir: string; + + before(async () => { + projectPath = await getFixtureProjectPath(projectName, changeDirTo); + prevWorkingDir = process.cwd(); + process.chdir(projectPath); + }); + + after(() => { + process.chdir(prevWorkingDir); + }); +} + +async function getFixtureProjectPath( + projectName: string, + changeDirTo?: string, +): Promise { + const normalizedProjectName = projectName.replaceAll("/", path.sep); + + let projectPath = path.join( + process.cwd(), + "test", + "fixture-projects", + normalizedProjectName, + ); + + if (changeDirTo !== undefined) { + projectPath = path.join(projectPath, changeDirTo); + } + + if (!(await exists(projectPath))) { + throw new Error(`Fixture project ${projectName} doesn't exist`); + } + + return getRealPath(projectPath); +} diff --git a/v-next/hardhat-test-utils/src/index.ts b/v-next/hardhat-test-utils/src/index.ts index 4a25dd7f31..70c40e300d 100644 --- a/v-next/hardhat-test-utils/src/index.ts +++ b/v-next/hardhat-test-utils/src/index.ts @@ -1 +1,2 @@ +export * from "./fixture-projects.js"; export * from "./hardhat-error.js"; From 5cc481d0502f5d39414ae202f5c5fc4cf899046f Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Wed, 21 Aug 2024 02:32:52 -0400 Subject: [PATCH 4/9] update all fixture project tests to use hardhat-test-utils version --- v-next/hardhat/test/helpers/project.ts | 53 ------------------- v-next/hardhat/test/hre/index.ts | 6 ++- .../builtin-plugins/clean/task-action.ts | 2 +- .../builtin-plugins/console/task-action.ts | 2 +- .../builtin-plugins/run/task-action.ts | 6 ++- v-next/hardhat/test/internal/cli/init/init.ts | 6 ++- v-next/hardhat/test/internal/cli/main.ts | 2 +- 7 files changed, 15 insertions(+), 62 deletions(-) delete mode 100644 v-next/hardhat/test/helpers/project.ts diff --git a/v-next/hardhat/test/helpers/project.ts b/v-next/hardhat/test/helpers/project.ts deleted file mode 100644 index 34eb749cce..0000000000 --- a/v-next/hardhat/test/helpers/project.ts +++ /dev/null @@ -1,53 +0,0 @@ -import path from "node:path"; -import { before, after } from "node:test"; - -import { exists, getRealPath } from "@ignored/hardhat-vnext-utils/fs"; - -/** - * This helper adds mocha hooks to run the tests inside one of the projects - * from test/fixture-projects. - * - * @param projectName The base name of the folder with the project to use. - * @param changeDirTo If provided, the working directory will be changed to this. Must be a child of the project folder. - */ -export function useFixtureProject( - projectName: string, - changeDirTo?: string, -): void { - let projectPath: string; - let prevWorkingDir: string; - - before(async () => { - projectPath = await getFixtureProjectPath(projectName, changeDirTo); - prevWorkingDir = process.cwd(); - process.chdir(projectPath); - }); - - after(() => { - process.chdir(prevWorkingDir); - }); -} - -async function getFixtureProjectPath( - projectName: string, - changeDirTo?: string, -): Promise { - const normalizedProjectName = projectName.replaceAll("/", path.sep); - - let projectPath = path.join( - import.meta.dirname, - "..", - "fixture-projects", - normalizedProjectName, - ); - - if (changeDirTo !== undefined) { - projectPath = path.join(projectPath, changeDirTo); - } - - if (!(await exists(projectPath))) { - throw new Error(`Fixture project ${projectName} doesn't exist`); - } - - return getRealPath(projectPath); -} diff --git a/v-next/hardhat/test/hre/index.ts b/v-next/hardhat/test/hre/index.ts index c7394a0371..6602cd4ffa 100644 --- a/v-next/hardhat/test/hre/index.ts +++ b/v-next/hardhat/test/hre/index.ts @@ -2,7 +2,10 @@ import assert from "node:assert/strict"; import { afterEach, describe, it } from "node:test"; import { HardhatError } from "@ignored/hardhat-vnext-errors"; -import { assertRejectsWithHardhatError } from "@nomicfoundation/hardhat-test-utils"; +import { + assertRejectsWithHardhatError, + useFixtureProject, +} from "@nomicfoundation/hardhat-test-utils"; import { resolveHardhatConfigPath } from "../../src/config.js"; import { createHardhatRuntimeEnvironment } from "../../src/hre.js"; @@ -12,7 +15,6 @@ import { resetGlobalHardhatRuntimeEnvironment, setGlobalHardhatRuntimeEnvironment, } from "../../src/internal/global-hre-instance.js"; -import { useFixtureProject } from "../helpers/project.js"; describe("HRE", () => { afterEach(() => { diff --git a/v-next/hardhat/test/internal/builtin-plugins/clean/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/clean/task-action.ts index 8ef0324283..a3bc6f8511 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/clean/task-action.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/clean/task-action.ts @@ -12,10 +12,10 @@ import { remove, writeUtf8File, } from "@ignored/hardhat-vnext-utils/fs"; +import { useFixtureProject } from "@nomicfoundation/hardhat-test-utils"; import { createHardhatRuntimeEnvironment } from "../../../../src/hre.js"; import cleanAction from "../../../../src/internal/builtin-plugins/clean/task-action.js"; -import { useFixtureProject } from "../../../helpers/project.js"; let hre: HardhatRuntimeEnvironment; let globalCacheDir: string; diff --git a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts index 416925ea3e..741a54c43c 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/console/task-action.ts @@ -10,11 +10,11 @@ import { afterEach, before, beforeEach, describe, it } from "node:test"; import { ensureError } from "@ignored/hardhat-vnext-utils/error"; import { exists, remove } from "@ignored/hardhat-vnext-utils/fs"; +import { useFixtureProject } from "@nomicfoundation/hardhat-test-utils"; import debug from "debug"; import { createHardhatRuntimeEnvironment } from "../../../../src/hre.js"; import consoleAction from "../../../../src/internal/builtin-plugins/console/task-action.js"; -import { useFixtureProject } from "../../../helpers/project.js"; const log = debug("hardhat:test:console:task-action"); diff --git a/v-next/hardhat/test/internal/builtin-plugins/run/task-action.ts b/v-next/hardhat/test/internal/builtin-plugins/run/task-action.ts index 85caa2ef25..acf7d7feaf 100644 --- a/v-next/hardhat/test/internal/builtin-plugins/run/task-action.ts +++ b/v-next/hardhat/test/internal/builtin-plugins/run/task-action.ts @@ -3,11 +3,13 @@ import type { HardhatRuntimeEnvironment } from "@ignored/hardhat-vnext-core/type import { before, describe, it } from "node:test"; import { HardhatError } from "@ignored/hardhat-vnext-errors"; -import { assertRejectsWithHardhatError } from "@nomicfoundation/hardhat-test-utils"; +import { + assertRejectsWithHardhatError, + useFixtureProject, +} from "@nomicfoundation/hardhat-test-utils"; import { createHardhatRuntimeEnvironment } from "../../../../src/hre.js"; import runScriptWithHardhat from "../../../../src/internal/builtin-plugins/run/task-action.js"; -import { useFixtureProject } from "../../../helpers/project.js"; describe("run/task-action", function () { let hre: HardhatRuntimeEnvironment; diff --git a/v-next/hardhat/test/internal/cli/init/init.ts b/v-next/hardhat/test/internal/cli/init/init.ts index a578d4edbb..4d860b4622 100644 --- a/v-next/hardhat/test/internal/cli/init/init.ts +++ b/v-next/hardhat/test/internal/cli/init/init.ts @@ -8,12 +8,14 @@ import { readUtf8File, remove, } from "@ignored/hardhat-vnext-utils/fs"; -import { assertRejectsWithHardhatError } from "@nomicfoundation/hardhat-test-utils"; +import { + assertRejectsWithHardhatError, + useFixtureProject, +} from "@nomicfoundation/hardhat-test-utils"; import { initHardhat } from "../../../../src/internal/cli/init/init.js"; import { EMPTY_HARDHAT_CONFIG } from "../../../../src/internal/cli/init/sample-config-file.js"; import { findClosestHardhatConfig } from "../../../../src/internal/helpers/config-loading.js"; -import { useFixtureProject } from "../../../helpers/project.js"; async function deleteHardhatConfigFile() { await remove(path.join(process.cwd(), "hardhat.config.ts")); diff --git a/v-next/hardhat/test/internal/cli/main.ts b/v-next/hardhat/test/internal/cli/main.ts index 8800aa764d..8b69ed1de5 100644 --- a/v-next/hardhat/test/internal/cli/main.ts +++ b/v-next/hardhat/test/internal/cli/main.ts @@ -25,6 +25,7 @@ import { isCi } from "@ignored/hardhat-vnext-utils/ci"; import { assertThrowsHardhatError, assertRejectsWithHardhatError, + useFixtureProject, } from "@nomicfoundation/hardhat-test-utils"; import chalk from "chalk"; @@ -38,7 +39,6 @@ import { } from "../../../src/internal/cli/main.js"; import { resetGlobalHardhatRuntimeEnvironment } from "../../../src/internal/global-hre-instance.js"; import { getHardhatVersion } from "../../../src/internal/utils/package.js"; -import { useFixtureProject } from "../../helpers/project.js"; async function getTasksAndHreEnvironment( tasksBuilders: NewTaskDefinitionBuilder[], From 2f16b74b3d66f544c457d83ac1895598160665c3 Mon Sep 17 00:00:00 2001 From: Zoey Date: Mon, 26 Aug 2024 02:07:27 -0400 Subject: [PATCH 5/9] Update v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts Co-authored-by: Patricio Palladino --- v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts b/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts index 225e84097a..47c414b599 100644 --- a/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts +++ b/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts @@ -68,6 +68,7 @@ export default async (): Promise> => { return { ...resolvedConfig, mocha: { + timeout: 40000, ...resolvedConfig.mocha, ...userConfig.mocha, }, From 55f1f31a09e3386153c7c6d58cb00de08418a53d Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Mon, 26 Aug 2024 02:24:01 -0400 Subject: [PATCH 6/9] add config validation test --- .../src/hookHandlers/config.ts | 6 ++++- .../invalid-mocha-config/hardhat.config.js | 10 ++++++++ .../invalid-mocha-config/test/test.ts | 15 ++++++++++++ .../hardhat-mocha-test-runner/test/index.ts | 23 +++++++++++++++---- 4 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.js create mode 100644 v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/test/test.ts diff --git a/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts b/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts index 47c414b599..2f2f2a2421 100644 --- a/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts +++ b/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts @@ -50,10 +50,14 @@ const mochaConfigType = z.object({ isWorker: z.boolean().optional(), }); +const userConfigType = z.object({ + mocha: z.optional(mochaConfigType), +}); + export default async (): Promise> => { const handlers: Partial = { validateUserConfig: async (userConfig) => { - return validateUserConfigZodType(userConfig, mochaConfigType); + return validateUserConfigZodType(userConfig, userConfigType); }, resolveUserConfig: async ( userConfig, diff --git a/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.js b/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.js new file mode 100644 index 0000000000..3d0ea6e7c6 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.js @@ -0,0 +1,10 @@ +import HardhatMochaPlugin from "../../../src/index.js"; + +const config = { + plugins: [HardhatMochaPlugin], + mocha: { + delay: 123, + }, +}; + +export default config; diff --git a/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/test/test.ts b/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/test/test.ts new file mode 100644 index 0000000000..05185a516f --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/test/test.ts @@ -0,0 +1,15 @@ +import assert from "node:assert/strict"; + +import hre from "@ignored/hardhat-vnext"; +import { describe, it } from "mocha"; + +describe("Mocha test", () => { + it("should work", () => { + assert.equal(1 + 1, 2); + }); + + it("should have the test task", () => { + // throws if the task doesn't exist + hre.tasks.getTask("test"); + }); +}); diff --git a/v-next/hardhat-mocha-test-runner/test/index.ts b/v-next/hardhat-mocha-test-runner/test/index.ts index 1011c31768..de1f713be2 100644 --- a/v-next/hardhat-mocha-test-runner/test/index.ts +++ b/v-next/hardhat-mocha-test-runner/test/index.ts @@ -4,13 +4,26 @@ import { describe, it } from "node:test"; import { useFixtureProject } from "@nomicfoundation/hardhat-test-utils"; describe("Hardhat Mocha plugin", () => { - useFixtureProject("test-project"); + describe("Success", () => { + useFixtureProject("test-project"); - it("should work", async () => { - const hre = await import("@ignored/hardhat-vnext"); + it("should work", async () => { + const hre = await import("@ignored/hardhat-vnext"); - const result = await hre.tasks.getTask("test").run({}); + const result = await hre.tasks.getTask("test").run({}); - assert.equal(result, 0); + assert.equal(result, 0); + }); + }); + + describe("Failure", () => { + useFixtureProject("invalid-mocha-config"); + + it("should fail", async () => { + await assert.rejects( + () => import("@ignored/hardhat-vnext"), + /Config error in config\.mocha\.delay: Expected boolean, received number/, + ); + }); }); }); From 9e776ecbf753b0eccc4ed8d70bf7f46d66d3b8b1 Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Mon, 26 Aug 2024 03:49:55 -0400 Subject: [PATCH 7/9] export hre reset function for plugin testing --- .../invalid-mocha-config/hardhat.config.js | 10 ------ .../invalid-mocha-config/hardhat.config.ts | 13 +++++++ .../hardhat-mocha-test-runner/test/index.ts | 35 ++++++++++++++++--- v-next/hardhat/src/index.ts | 9 +++++ 4 files changed, 52 insertions(+), 15 deletions(-) delete mode 100644 v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.js create mode 100644 v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.ts diff --git a/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.js b/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.js deleted file mode 100644 index 3d0ea6e7c6..0000000000 --- a/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.js +++ /dev/null @@ -1,10 +0,0 @@ -import HardhatMochaPlugin from "../../../src/index.js"; - -const config = { - plugins: [HardhatMochaPlugin], - mocha: { - delay: 123, - }, -}; - -export default config; diff --git a/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.ts b/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.ts new file mode 100644 index 0000000000..ebfe09e2e2 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/test/fixture-projects/invalid-mocha-config/hardhat.config.ts @@ -0,0 +1,13 @@ +import type { HardhatUserConfig } from "@ignored/hardhat-vnext/config"; + +import HardhatMochaPlugin from "../../../src/index.js"; + +const config: HardhatUserConfig = { + plugins: [HardhatMochaPlugin], + mocha: { + // @ts-expect-error -- testing config validation + delay: 123, + }, +}; + +export default config; diff --git a/v-next/hardhat-mocha-test-runner/test/index.ts b/v-next/hardhat-mocha-test-runner/test/index.ts index de1f713be2..aed0b13dbf 100644 --- a/v-next/hardhat-mocha-test-runner/test/index.ts +++ b/v-next/hardhat-mocha-test-runner/test/index.ts @@ -1,12 +1,24 @@ import assert from "node:assert/strict"; -import { describe, it } from "node:test"; +import { describe, it, afterEach } from "node:test"; -import { useFixtureProject } from "@nomicfoundation/hardhat-test-utils"; +import { HardhatError } from "@ignored/hardhat-vnext-errors"; +import { + assertRejectsWithHardhatError, + useFixtureProject, +} from "@nomicfoundation/hardhat-test-utils"; describe("Hardhat Mocha plugin", () => { describe("Success", () => { useFixtureProject("test-project"); + afterEach(async () => { + const { _resetGlobalHardhatRuntimeEnvironment } = await import( + "@ignored/hardhat-vnext" + ); + + _resetGlobalHardhatRuntimeEnvironment(); + }); + it("should work", async () => { const hre = await import("@ignored/hardhat-vnext"); @@ -19,10 +31,23 @@ describe("Hardhat Mocha plugin", () => { describe("Failure", () => { useFixtureProject("invalid-mocha-config"); + afterEach(async () => { + const { _resetGlobalHardhatRuntimeEnvironment } = await import( + "@ignored/hardhat-vnext" + ); + + _resetGlobalHardhatRuntimeEnvironment(); + }); + it("should fail", async () => { - await assert.rejects( - () => import("@ignored/hardhat-vnext"), - /Config error in config\.mocha\.delay: Expected boolean, received number/, + const errors = + "\t* Config error in config.mocha.delay: Expected boolean, received number"; + + await assertRejectsWithHardhatError( + // @ts-expect-error -- we need to invalidate the import cache to re-import the HRE + import("@ignored/hardhat-vnext?config=invalid"), + HardhatError.ERRORS.GENERAL.INVALID_CONFIG, + { errors }, ); }); }); diff --git a/v-next/hardhat/src/index.ts b/v-next/hardhat/src/index.ts index 67e1b3b110..31421d3718 100644 --- a/v-next/hardhat/src/index.ts +++ b/v-next/hardhat/src/index.ts @@ -12,6 +12,7 @@ import { createHardhatRuntimeEnvironment } from "./hre.js"; import { getGlobalHardhatRuntimeEnvironment, setGlobalHardhatRuntimeEnvironment, + resetGlobalHardhatRuntimeEnvironment, } from "./internal/global-hre-instance.js"; import { importUserConfig } from "./internal/helpers/config-loading.js"; @@ -38,4 +39,12 @@ export const globalOptions: GlobalOptions = hre.globalOptions; export const hooks: HookManager = hre.hooks; export const interruptions: UserInterruptionManager = hre.interruptions; +// We need to re-export this function so that plugins can use it. +export const _resetGlobalHardhatRuntimeEnvironment: typeof resetGlobalHardhatRuntimeEnvironment = + function (): void { + maybeHre = undefined; + + resetGlobalHardhatRuntimeEnvironment(); + }; + export default hre; From 06c7f9b1587ebe5696cbd1861cbe85bf3d9dff9e Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Mon, 26 Aug 2024 11:11:06 -0400 Subject: [PATCH 8/9] add mocha issue to error descriptor --- v-next/hardhat-errors/src/descriptors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v-next/hardhat-errors/src/descriptors.ts b/v-next/hardhat-errors/src/descriptors.ts index eafc52d97e..72f7c31e0e 100644 --- a/v-next/hardhat-errors/src/descriptors.ts +++ b/v-next/hardhat-errors/src/descriptors.ts @@ -478,7 +478,7 @@ Please check Hardhat's output for more details.`, messageTemplate: `Your project uses ESM and you've programmatically run your tests twice. This is not supported yet.`, websiteTitle: "Running tests twice in an ESM project", websiteDescription: - 'You have run your tests twice programmatically and your project is an ESM project (you have `"type": "module"` in your `package.json`, or some of your files have the `.mjs` extension). This is not supported by Mocha yet.', + 'You have run your tests twice programmatically and your project is an ESM project (you have `"type": "module"` in your `package.json`, or some of your files have the `.mjs` extension). This is not supported by Mocha yet (https://github.com/mochajs/mocha/issues/2706).', }, }, NETWORK: { From 7ee9f049b2962f60c141365e12f56d9b5ff2766c Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Tue, 27 Aug 2024 02:46:10 -0400 Subject: [PATCH 9/9] create hre locally in test --- pnpm-lock.yaml | 4 --- v-next/example-project/package.json | 3 -- .../hardhat-mocha-test-runner/test/index.ts | 33 +++++++++---------- v-next/hardhat-zod-utils/package.json | 1 - v-next/hardhat/src/index.ts | 9 ----- 5 files changed, 16 insertions(+), 34 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7dda362330..4363fc853b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1506,10 +1506,6 @@ importers: version: 7.7.1(eslint@8.57.0)(typescript@5.5.4) v-next/example-project: - dependencies: - tsx: - specifier: ^4.11.0 - version: 4.16.5 devDependencies: '@ignored/hardhat-vnext': specifier: workspace:^3.0.0-next.3 diff --git a/v-next/example-project/package.json b/v-next/example-project/package.json index 4598e79d90..19d09735d3 100644 --- a/v-next/example-project/package.json +++ b/v-next/example-project/package.json @@ -29,8 +29,5 @@ "mocha": "^10.0.0", "prettier": "3.2.5", "typescript": "~5.5.0" - }, - "dependencies": { - "tsx": "^4.11.0" } } diff --git a/v-next/hardhat-mocha-test-runner/test/index.ts b/v-next/hardhat-mocha-test-runner/test/index.ts index aed0b13dbf..6757df428a 100644 --- a/v-next/hardhat-mocha-test-runner/test/index.ts +++ b/v-next/hardhat-mocha-test-runner/test/index.ts @@ -1,5 +1,5 @@ import assert from "node:assert/strict"; -import { describe, it, afterEach } from "node:test"; +import { describe, it } from "node:test"; import { HardhatError } from "@ignored/hardhat-vnext-errors"; import { @@ -11,16 +11,16 @@ describe("Hardhat Mocha plugin", () => { describe("Success", () => { useFixtureProject("test-project"); - afterEach(async () => { - const { _resetGlobalHardhatRuntimeEnvironment } = await import( - "@ignored/hardhat-vnext" + it("should work", async () => { + const { createHardhatRuntimeEnvironment } = await import( + "@ignored/hardhat-vnext/hre" ); - _resetGlobalHardhatRuntimeEnvironment(); - }); + const hardhatConfig = await import( + "./fixture-projects/test-project/hardhat.config.js" + ); - it("should work", async () => { - const hre = await import("@ignored/hardhat-vnext"); + const hre = await createHardhatRuntimeEnvironment(hardhatConfig.default); const result = await hre.tasks.getTask("test").run({}); @@ -31,21 +31,20 @@ describe("Hardhat Mocha plugin", () => { describe("Failure", () => { useFixtureProject("invalid-mocha-config"); - afterEach(async () => { - const { _resetGlobalHardhatRuntimeEnvironment } = await import( - "@ignored/hardhat-vnext" + it("should fail", async () => { + const { createHardhatRuntimeEnvironment } = await import( + "@ignored/hardhat-vnext/hre" ); - _resetGlobalHardhatRuntimeEnvironment(); - }); - - it("should fail", async () => { const errors = "\t* Config error in config.mocha.delay: Expected boolean, received number"; + const hardhatConfig = await import( + "./fixture-projects/invalid-mocha-config/hardhat.config.js" + ); + await assertRejectsWithHardhatError( - // @ts-expect-error -- we need to invalidate the import cache to re-import the HRE - import("@ignored/hardhat-vnext?config=invalid"), + createHardhatRuntimeEnvironment(hardhatConfig.default), HardhatError.ERRORS.GENERAL.INVALID_CONFIG, { errors }, ); diff --git a/v-next/hardhat-zod-utils/package.json b/v-next/hardhat-zod-utils/package.json index df64b2ef02..6578cd2a8e 100644 --- a/v-next/hardhat-zod-utils/package.json +++ b/v-next/hardhat-zod-utils/package.json @@ -61,7 +61,6 @@ "zod": "^3.23.8" }, "peerDependencies": { - "@ignored/hardhat-vnext-core": "workspace:^3.0.0-next.2", "zod": "^3.23.8" } } diff --git a/v-next/hardhat/src/index.ts b/v-next/hardhat/src/index.ts index 31421d3718..67e1b3b110 100644 --- a/v-next/hardhat/src/index.ts +++ b/v-next/hardhat/src/index.ts @@ -12,7 +12,6 @@ import { createHardhatRuntimeEnvironment } from "./hre.js"; import { getGlobalHardhatRuntimeEnvironment, setGlobalHardhatRuntimeEnvironment, - resetGlobalHardhatRuntimeEnvironment, } from "./internal/global-hre-instance.js"; import { importUserConfig } from "./internal/helpers/config-loading.js"; @@ -39,12 +38,4 @@ export const globalOptions: GlobalOptions = hre.globalOptions; export const hooks: HookManager = hre.hooks; export const interruptions: UserInterruptionManager = hre.interruptions; -// We need to re-export this function so that plugins can use it. -export const _resetGlobalHardhatRuntimeEnvironment: typeof resetGlobalHardhatRuntimeEnvironment = - function (): void { - maybeHre = undefined; - - resetGlobalHardhatRuntimeEnvironment(); - }; - export default hre;