Skip to content

Commit

Permalink
Merge pull request #797 from gemini-testing/HERMIONE-1210.require_loc…
Browse files Browse the repository at this point in the history
…al_files

fix: ability to load local files through cli option "require"
  • Loading branch information
DudaGod authored Oct 20, 2023
2 parents bb4a8d6 + cdd8772 commit 42ee492
Show file tree
Hide file tree
Showing 8 changed files with 134 additions and 18 deletions.
13 changes: 8 additions & 5 deletions src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const info = require("./info");
const { Hermione } = require("../hermione");
const pkg = require("../../package.json");
const logger = require("../utils/logger");
const { requireModule } = require("../utils/module");

let hermione;

Expand Down Expand Up @@ -58,7 +59,7 @@ exports.run = () => {
.arguments("[paths...]")
.action(async paths => {
try {
handleRequires(program.require);
await handleRequires(program.require);

const isTestsSuccess = await hermione.run(paths, {
reporters: program.reporter || defaults.reporters,
Expand Down Expand Up @@ -99,10 +100,6 @@ function preparseOption(program, option) {
return configFileParser[option];
}

function handleRequires(requires = []) {
requires.forEach(module => require(module));
}

function compileGrep(grep) {
try {
return new RegExp(`(${grep})|(${escapeRe(grep)})`);
Expand All @@ -111,3 +108,9 @@ function compileGrep(grep) {
return new RegExp(escapeRe(grep));
}
}

async function handleRequires(requires = []) {
for (const modulePath of requires) {
await requireModule(modulePath);
}
}
10 changes: 10 additions & 0 deletions src/utils/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import fs from "fs";

export const exists = async (path: string): Promise<boolean> => {
try {
await fs.promises.access(path);
return true;
} catch (_) {
return false;
}
};
8 changes: 8 additions & 0 deletions src/utils/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import path from "path";
import { exists } from "./fs";

export const requireModule = async <T = unknown>(modulePath: string): Promise<T> => {
const isModuleLocal = await exists(modulePath);

return require(isModuleLocal ? path.resolve(modulePath) : modulePath);
};
13 changes: 10 additions & 3 deletions src/worker/hermione-facade.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const Promise = require("bluebird");
const debug = require("debug")(`hermione:worker:${process.pid}`);
const ipc = require("../utils/ipc");
const { MASTER_INIT, MASTER_SYNC_CONFIG, WORKER_INIT, WORKER_SYNC_CONFIG } = require("../constants/process-messages");
const { requireModule } = require("../utils/module");

module.exports = class HermioneFacade {
static create() {
Expand Down Expand Up @@ -45,15 +46,21 @@ module.exports = class HermioneFacade {

ipc.on(MASTER_INIT, ({ configPath, runtimeConfig } = {}) => {
try {
const promise = Promise.resolve();

if (runtimeConfig.requireModules) {
runtimeConfig.requireModules.forEach(module => require(module));
runtimeConfig.requireModules.forEach(modulePath => {
promise.then(requireModule(modulePath));
});
}

RuntimeConfig.getInstance().extend(runtimeConfig);
const hermione = Hermione.create(configPath);

debug("worker initialized");
resolve(hermione);
promise.then(() => {
debug("worker initialized");
resolve(hermione);
});
} catch (e) {
debug("worker initialization failed");
reject(e);
Expand Down
10 changes: 6 additions & 4 deletions test/src/cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ describe("cli", () => {
});

it('should require modules specified in "require" option', async () => {
const fooRequire = sandbox.stub().returns({});
const requireModule = sandbox.stub();
const stubHermioneCli = proxyquire("src/cli", {
foo: (() => fooRequire())(),
"../utils/module": { requireModule },
});

await run_("--require foo", stubHermioneCli);

assert.calledOnce(fooRequire);
assert.calledOnceWith(requireModule, "foo");
});

it("should create Hermione without config by default", async () => {
Expand Down Expand Up @@ -155,7 +155,9 @@ describe("cli", () => {
});

it("should use require modules from cli", async () => {
const stubHermioneCli = proxyquire("src/cli", { foo: {} });
const stubHermioneCli = proxyquire("src/cli", {
"../utils/module": { requireModule: sandbox.stub() },
});
await run_("--require foo", stubHermioneCli);

assert.calledWithMatch(Hermione.prototype.run, any, { requireModules: ["foo"] });
Expand Down
33 changes: 33 additions & 0 deletions test/src/utils/fs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import fs from "fs";
import sinon, { SinonStub } from "sinon";
import { exists } from "../../../src/utils/fs";

describe("utils/fs", () => {
const sandbox = sinon.createSandbox();

beforeEach(() => {
sandbox.stub(fs.promises, "access");
});

afterEach(() => sandbox.restore());

describe("exists", () => {
it("should return 'true' if file exists", async () => {
const path = "./some-path.js";
(fs.promises.access as SinonStub).withArgs(path).resolves();

const isExists = await exists(path);

assert.isTrue(isExists);
});

it("should return 'false' if file doesn't exists", async () => {
const path = "./some-path.js";
(fs.promises.access as SinonStub).withArgs(path).rejects(new Error("o.O"));

const isExists = await exists(path);

assert.isFalse(isExists);
});
});
});
52 changes: 52 additions & 0 deletions test/src/utils/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import path from "path";
import sinon, { SinonStub } from "sinon";
import proxyquire from "proxyquire";
import { requireModule as realRequireModule } from "../../../src/utils/module";

describe("utils/module", () => {
let isNodeModuleRequired: SinonStub;
let isLocalModuleRequired: SinonStub;
let exists: SinonStub;
let requireModule: typeof realRequireModule;

const sandbox = sinon.createSandbox();
const nodeModulePath = "foo-module";
const relativeLocalModulePath = "./bar-module";
const absoluteLocalModulePath = path.resolve(relativeLocalModulePath);

beforeEach(() => {
isNodeModuleRequired = sinon.stub();
isLocalModuleRequired = sinon.stub();
exists = sinon.stub();

({ requireModule } = proxyquire.noCallThru().load("../../../src/utils/module", {
"./fs": { exists },
[nodeModulePath]: isNodeModuleRequired,
[absoluteLocalModulePath]: isLocalModuleRequired,
}));
});

afterEach(() => sandbox.restore());

describe("requireModule", () => {
it("should require module from node-modules", async () => {
exists.withArgs(nodeModulePath).resolves(false);
const module = await requireModule<SinonStub>(nodeModulePath);

module();

assert.calledOnce(isNodeModuleRequired);
assert.notCalled(isLocalModuleRequired);
});

it("should require module from node-modules", async () => {
exists.withArgs(relativeLocalModulePath).resolves(true);
const module = await requireModule<SinonStub>(relativeLocalModulePath);

module();

assert.calledOnce(isLocalModuleRequired);
assert.notCalled(isNodeModuleRequired);
});
});
});
13 changes: 7 additions & 6 deletions test/src/worker/hermione-facade.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";

const proxyquire = require("proxyquire");
const { AsyncEmitter } = require("src/events/async-emitter");
const { Hermione } = require("src/worker/hermione");
const { makeConfigStub } = require("../../utils");
Expand Down Expand Up @@ -46,18 +47,18 @@ describe("worker/hermione-facade", () => {
});

it("should require passed modules", async () => {
const hermioneFacadeModule = module.children.find(({ filename }) =>
/\/hermione-facade\.js$/.test(filename),
);
sandbox.stub(hermioneFacadeModule, "require");
const requireModule = sinon.stub();
const HermioneFacadeModule = proxyquire("src/worker/hermione-facade", {
"../utils/module": { requireModule },
});

ipc.on.withArgs(MASTER_INIT).yieldsAsync({
runtimeConfig: { requireModules: ["foo"] },
});

await hermioneFacade.init();
await HermioneFacadeModule.create().init();

assert.calledOnceWith(hermioneFacadeModule.require, "foo");
assert.calledOnceWith(requireModule, "foo");
});
});

Expand Down

0 comments on commit 42ee492

Please sign in to comment.