diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9862e400f2..4363fc853b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1506,20 +1506,25 @@ 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 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 +1817,82 @@ 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': + specifier: workspace:^3.0.0-next.2 + version: link:../hardhat + '@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 + '@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 + 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..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,9 +7,9 @@ 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"; const exampleEmptyTask = emptyTask("empty", "An example empty task").build(); @@ -102,8 +104,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..19d09735d3 100644 --- a/v-next/example-project/package.json +++ b/v-next/example-project/package.json @@ -22,12 +22,12 @@ }, "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" - }, - "dependencies": { - "tsx": "^4.11.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..fe6bb98d3b --- /dev/null +++ b/v-next/example-project/test/mocha/other-mocha-test.ts @@ -0,0 +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")); + }); +}); 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/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-errors/src/descriptors.ts b/v-next/hardhat-errors/src/descriptors.ts index 23068c5426..72f7c31e0e 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 (https://github.com/mochajs/mocha/issues/2706).', + }, }, 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..6a4ca9b05f --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/package.json @@ -0,0 +1,69 @@ +{ + "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": "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", + "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..2f2f2a2421 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/src/hookHandlers/config.ts @@ -0,0 +1,84 @@ +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(), +}); + +const userConfigType = z.object({ + mocha: z.optional(mochaConfigType), +}); + +export default async (): Promise> => { + const handlers: Partial = { + validateUserConfig: async (userConfig) => { + return validateUserConfigZodType(userConfig, userConfigType); + }, + resolveUserConfig: async ( + userConfig, + resolveConfigurationVariable, + next, + ) => { + const resolvedConfig = await next( + userConfig, + resolveConfigurationVariable, + ); + + return { + ...resolvedConfig, + mocha: { + timeout: 40000, + ...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 new file mode 100644 index 0000000000..5ca74c73f2 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/src/index.ts @@ -0,0 +1,33 @@ +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: [ + task("test", "Runs tests using the Mocha test runner") + .addVariadicArgument({ + name: "testFiles", + 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(), + ], + hookHandlers: { + config: import.meta.resolve("./hookHandlers/config.js"), + }, +}; + +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..f9c1a85c60 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/src/task-action.ts @@ -0,0 +1,94 @@ +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 { getAllFilesMatching } from "@ignored/hardhat-vnext-utils/fs"; + +interface TestActionArguments { + testFiles: string[]; + bail: boolean; + grep: 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), + ); +} + +let testsAlreadyRun = false; +const testWithHardhat: NewTaskActionFunction = async ( + { testFiles, bail, grep }, + hre, +) => { + const files = await getTestFiles(testFiles, hre.config); + + 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)); + + // 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(); + + 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 new file mode 100644 index 0000000000..097686e986 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/src/type-extensions.ts @@ -0,0 +1,13 @@ +import "@ignored/hardhat-vnext/types/config"; + +import type { MochaOptions } from "mocha"; + +declare module "@ignored/hardhat-vnext/types/config" { + export interface HardhatUserConfig { + mocha?: MochaOptions; + } + + export interface HardhatConfig { + mocha: MochaOptions; + } +} 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/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/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 new file mode 100644 index 0000000000..6757df428a --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/test/index.ts @@ -0,0 +1,53 @@ +import assert from "node:assert/strict"; +import { describe, it } from "node:test"; + +import { HardhatError } from "@ignored/hardhat-vnext-errors"; +import { + assertRejectsWithHardhatError, + useFixtureProject, +} from "@nomicfoundation/hardhat-test-utils"; + +describe("Hardhat Mocha plugin", () => { + describe("Success", () => { + useFixtureProject("test-project"); + + it("should work", async () => { + const { createHardhatRuntimeEnvironment } = await import( + "@ignored/hardhat-vnext/hre" + ); + + const hardhatConfig = await import( + "./fixture-projects/test-project/hardhat.config.js" + ); + + const hre = await createHardhatRuntimeEnvironment(hardhatConfig.default); + + const result = await hre.tasks.getTask("test").run({}); + + assert.equal(result, 0); + }); + }); + + describe("Failure", () => { + useFixtureProject("invalid-mocha-config"); + + it("should fail", async () => { + const { createHardhatRuntimeEnvironment } = await import( + "@ignored/hardhat-vnext/hre" + ); + + 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( + createHardhatRuntimeEnvironment(hardhatConfig.default), + HardhatError.ERRORS.GENERAL.INVALID_CONFIG, + { errors }, + ); + }); + }); +}); 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..47b339df71 --- /dev/null +++ b/v-next/hardhat-mocha-test-runner/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../config-v-next/tsconfig.json", + "references": [ + { + "path": "../hardhat" + }, + { + "path": "../hardhat-errors" + }, + { + "path": "../hardhat-node-test-reporter" + }, + { + "path": "../hardhat-test-utils" + }, + { + "path": "../hardhat-utils" + } + ] +} diff --git a/v-next/hardhat/test/helpers/project.ts b/v-next/hardhat-test-utils/src/fixture-projects.ts similarity index 86% rename from v-next/hardhat/test/helpers/project.ts rename to v-next/hardhat-test-utils/src/fixture-projects.ts index 34eb749cce..afb70e3ffe 100644 --- a/v-next/hardhat/test/helpers/project.ts +++ b/v-next/hardhat-test-utils/src/fixture-projects.ts @@ -4,8 +4,8 @@ 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. + * 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. @@ -35,8 +35,8 @@ async function getFixtureProjectPath( const normalizedProjectName = projectName.replaceAll("/", path.sep); let projectPath = path.join( - import.meta.dirname, - "..", + process.cwd(), + "test", "fixture-projects", normalizedProjectName, ); 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"; 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/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[],