Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support ESM test running via the test task #3156

Merged
merged 6 commits into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 additions & 4 deletions packages/hardhat-core/src/builtin-tasks/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ import path from "path";

import { HARDHAT_NETWORK_NAME } from "../internal/constants";
import { subtask, task } from "../internal/core/config/config-env";
import { HardhatError } from "../internal/core/errors";
import { ERRORS } from "../internal/core/errors-list";
import { isRunningWithTypescript } from "../internal/core/typescript-support";
import { getForkCacheDirPath } from "../internal/hardhat-network/provider/utils/disk-cache";
import { showForkRecommendationsBannerIfNecessary } from "../internal/hardhat-network/provider/utils/fork-recomendations-banner";
import { pluralize } from "../internal/util/strings";
import { getAllFilesMatching } from "../internal/util/fs-utils";
import { getProjectPackageJson } from "../internal/util/packageInfo";

import {
TASK_COMPILE,
Expand All @@ -35,23 +38,26 @@ subtask(TASK_TEST_GET_TEST_FILES)
return testFilesAbsolutePaths;
}

const jsFiles = await getAllFilesMatching(config.paths.tests, (f) =>
f.endsWith(".js")
const jsFiles = await getAllFilesMatching(
config.paths.tests,
(f) => f.endsWith(".js") || f.endsWith(".cjs") || f.endsWith(".mjs")
);

if (!isRunningWithTypescript(config)) {
return jsFiles;
}

const tsFiles = await getAllFilesMatching(config.paths.tests, (f) =>
f.endsWith(".ts")
const tsFiles = await getAllFilesMatching(
config.paths.tests,
(f) => f.endsWith(".ts") || f.endsWith(".cts") || f.endsWith(".mts")
);

return [...jsFiles, ...tsFiles];
});

subtask(TASK_TEST_SETUP_TEST_ENVIRONMENT, async () => {});

let testsAlreadyRun = false;
subtask(TASK_TEST_RUN_MOCHA_TESTS)
.addFlag("parallel", "Run tests in parallel")
.addFlag("bail", "Stop running tests after the first test failure")
Expand Down Expand Up @@ -99,6 +105,29 @@ subtask(TASK_TEST_RUN_MOCHA_TESTS)
const mocha = new Mocha(mochaConfig);
taskArgs.testFiles.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 getProjectPackageJson();
const isTypeModule = projectPackageJson.type === "module";
const hasEsmTest = taskArgs.testFiles.some(
(file) => file.endsWith(".mjs") || file.endsWith(".mts")
);
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(
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<number>((resolve) => {
mocha.run(resolve);
});
Expand Down
13 changes: 11 additions & 2 deletions packages/hardhat-core/src/internal/core/config/config-loading.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,17 @@ import { DEFAULT_SOLC_VERSION } from "./default-config";
const log = debug("hardhat:core:config");

function importCsjOrEsModule(filePath: string): any {
const imported = require(filePath);
return imported.default !== undefined ? imported.default : imported;
try {
const imported = require(filePath);
return imported.default !== undefined ? imported.default : imported;
} catch (e: any) {
if (e.code === "ERR_REQUIRE_ESM") {
throw new HardhatError(ERRORS.GENERAL.ESM_PROJECT_WITHOUT_CJS_CONFIG);
}

// eslint-disable-next-line @nomiclabs/hardhat-internal-rules/only-hardhat-error
throw e;
}
}

export function resolveConfigPath(configPath: string | undefined) {
Expand Down
18 changes: 18 additions & 0 deletions packages/hardhat-core/src/internal/core/errors-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,16 @@ Either try using a new directory name, or remove the conflicting files.`,
"Hardhat attempted to convert the input value to a BigInt, but no known conversion method was applicable to the given value.",
shouldBeReported: false,
},
ESM_PROJECT_WITHOUT_CJS_CONFIG: {
number: 18,
message: `Your project is an ESM project (you have "type": "module" set in your package.json) but your Hardhat config file uses a .js or .ts extension.

Rename the file to use a .cjs or .cts extension to fix this problem.`,
title: "Hardht config with .js or .ts extension in an ESM project",
description:
"Your project is an ESM project but your Hardhat config uses an .js or .ts extension. Hardhat config files cannot be an ES module. To fix this, rename your Hardhat config to use the .cjs or .cts extension.",
shouldBeReported: false,
},
},
NETWORK: {
CONFIG_NOT_FOUND: {
Expand Down Expand Up @@ -947,6 +957,14 @@ The first supported version is %firstSupportedVersion%`,
Please use a newer, supported version.`,
shouldBeReported: true,
},
TEST_TASK_ESM_TESTS_RUN_TWICE: {
number: 609,
message: `Your project uses ESM and you've programmatically run your tests twice. This is not supported yet.`,
title: "Running tests twice in an ESM project",
description:
'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 has a `.mjs` or `.mts` extension). This is not supported by Mocha yet.',
shouldBeReported: true,
},
},
ARTIFACTS: {
NOT_FOUND: {
Expand Down
16 changes: 16 additions & 0 deletions packages/hardhat-core/src/internal/util/packageInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import findup from "find-up";
import fsExtra from "fs-extra";
import path from "path";

import { assertHardhatInvariant } from "../core/errors";

export function getPackageJsonPath(): string {
return findClosestPackageJson(__filename)!;
}
Expand Down Expand Up @@ -43,3 +45,17 @@ export function getHardhatVersion(): string | null {
return null;
}
}

/**
* Return the contents of the package.json in the user's project
*/
export function getProjectPackageJson(): Promise<any> {
const packageJsonPath = findup.sync("package.json");

assertHardhatInvariant(
packageJsonPath !== null,
"Expected a package.json file in the current directory or in an ancestor directory"
);

return fsExtra.readJson(packageJsonPath);
}
Loading