From 5b921302787a526377be02a37eb43a487c8f283d Mon Sep 17 00:00:00 2001 From: Nico Jansen Date: Wed, 18 Nov 2020 20:20:39 +0100 Subject: [PATCH] feat(karma-runner): resolve local karma and ng version (#2622) Require `karma` or `@angular/cli` from the current working directory, instead of from `@stryker-mutator/karma-runner/src/util.js` (where the old `requireModule` function lived). --- .../src/starters/angular-starter.ts | 8 +++--- .../src/starters/karma-starter.ts | 4 +-- .../src/starters/stryker-karma.conf.ts | 8 +++--- packages/karma-runner/src/utils.ts | 3 --- .../unit/starters/angular-starter.spec.ts | 4 +-- .../unit/starters/stryker-karma.conf.spec.ts | 4 +-- packages/util/.vscode/launch.json | 7 ++--- packages/util/package.json | 4 ++- packages/util/src/index.ts | 1 + packages/util/src/require-resolve.ts | 7 +++++ .../integration/require-resolve.it.spec.ts | 26 +++++++++++++++++++ packages/util/testResources/.gitignore | 1 + .../baz/node_modules/bar/index.js | 1 + .../foo/node_modules/bar/index.js | 1 + .../require-resolve/node_modules/bar/index.js | 1 + 15 files changed, 61 insertions(+), 19 deletions(-) delete mode 100644 packages/karma-runner/src/utils.ts create mode 100644 packages/util/src/require-resolve.ts create mode 100644 packages/util/test/integration/require-resolve.it.spec.ts create mode 100644 packages/util/testResources/.gitignore create mode 100644 packages/util/testResources/require-resolve/baz/node_modules/bar/index.js create mode 100644 packages/util/testResources/require-resolve/foo/node_modules/bar/index.js create mode 100644 packages/util/testResources/require-resolve/node_modules/bar/index.js diff --git a/packages/karma-runner/src/starters/angular-starter.ts b/packages/karma-runner/src/starters/angular-starter.ts index bb8224b796..fd287a8faa 100644 --- a/packages/karma-runner/src/starters/angular-starter.ts +++ b/packages/karma-runner/src/starters/angular-starter.ts @@ -4,8 +4,9 @@ import decamelize = require('decamelize'); import { Logger, LoggerFactoryMethod } from '@stryker-mutator/api/logging'; import * as semver from 'semver'; +import { requireResolve } from '@stryker-mutator/util'; + import { NgConfigOptions, NgTestArguments } from '../../src-generated/karma-runner-options'; -import { requireModule } from '../utils'; const MIN_ANGULAR_CLI_VERSION = '6.1.0'; @@ -14,7 +15,7 @@ export async function start(getLogger: LoggerFactoryMethod, ngConfig?: NgConfigO verifyAngularCliVersion(); // Make sure require angular cli from inside this function, that way it won't break if angular isn't installed and this file is required. - let cli = requireModule('@angular/cli'); + let cli: any = requireResolve('@angular/cli'); if ('default' in cli) { cli = cli.default; } @@ -47,7 +48,8 @@ export async function start(getLogger: LoggerFactoryMethod, ngConfig?: NgConfigO } function verifyAngularCliVersion() { - const version = semver.coerce(requireModule('@angular/cli/package').version); + const pkg = requireResolve('@angular/cli/package') as { version: string }; + const version = semver.coerce(pkg.version); if (!version || semver.lt(version, MIN_ANGULAR_CLI_VERSION)) { throw new Error(`Your @angular/cli version (${version}) is not supported. Please install ${MIN_ANGULAR_CLI_VERSION} or higher`); } diff --git a/packages/karma-runner/src/starters/karma-starter.ts b/packages/karma-runner/src/starters/karma-starter.ts index e361935cab..1cd252db9d 100644 --- a/packages/karma-runner/src/starters/karma-starter.ts +++ b/packages/karma-runner/src/starters/karma-starter.ts @@ -1,8 +1,8 @@ -import { requireModule } from '../utils'; +import { requireResolve } from '@stryker-mutator/util'; export async function start(): Promise { // Make sure require karma from inside this function, that way it won't break if karma isn't installed and this file is required. - const karma = requireModule('karma'); + const karma: any = requireResolve('karma'); await new karma.Server({ configFile: require.resolve('./stryker-karma.conf'), }).start(); diff --git a/packages/karma-runner/src/starters/stryker-karma.conf.ts b/packages/karma-runner/src/starters/stryker-karma.conf.ts index a7810e5c7d..f3d14bc380 100644 --- a/packages/karma-runner/src/starters/stryker-karma.conf.ts +++ b/packages/karma-runner/src/starters/stryker-karma.conf.ts @@ -2,11 +2,10 @@ import * as path from 'path'; import { Logger, LoggerFactoryMethod } from '@stryker-mutator/api/logging'; import { Config, ConfigOptions, ClientOptions, InlinePluginType } from 'karma'; -import { noopLogger } from '@stryker-mutator/util'; +import { noopLogger, requireResolve } from '@stryker-mutator/util'; import StrykerReporter from '../karma-plugins/stryker-reporter'; import TestHooksMiddleware, { TEST_HOOKS_FILE_NAME } from '../karma-plugins/test-hooks-middleware'; -import { requireModule } from '../utils'; function setDefaultOptions(config: Config) { config.set({ @@ -20,7 +19,10 @@ function setUserKarmaConfigFile(config: Config, log: Logger) { const configFileName = path.resolve(globalSettings.karmaConfigFile); log.debug('Importing config from "%s"', configFileName); try { - const userConfig = requireModule(configFileName); + const userConfig = requireResolve(configFileName); + if (typeof userConfig !== 'function') { + throw new TypeError(`Karma config file "${configFileName}" should export a function! Found: ${typeof userConfig}`); + } userConfig(config); config.configFile = configFileName; // override config to ensure karma is as user-like as possible } catch (error) { diff --git a/packages/karma-runner/src/utils.ts b/packages/karma-runner/src/utils.ts deleted file mode 100644 index 14103cee70..0000000000 --- a/packages/karma-runner/src/utils.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function requireModule(name: string): any { - return require(name); -} diff --git a/packages/karma-runner/test/unit/starters/angular-starter.spec.ts b/packages/karma-runner/test/unit/starters/angular-starter.spec.ts index 2c28d00f02..3320cdc3fd 100644 --- a/packages/karma-runner/test/unit/starters/angular-starter.spec.ts +++ b/packages/karma-runner/test/unit/starters/angular-starter.spec.ts @@ -1,10 +1,10 @@ import { LoggerFactoryMethod } from '@stryker-mutator/api/logging'; import { testInjector } from '@stryker-mutator/test-helpers'; +import * as utils from '@stryker-mutator/util'; import { expect } from 'chai'; import * as sinon from 'sinon'; import * as sut from '../../../src/starters/angular-starter'; -import * as utils from '../../../src/utils'; describe('angularStarter', () => { let requireModuleStub: sinon.SinonStub; @@ -13,7 +13,7 @@ describe('angularStarter', () => { beforeEach(() => { cliStub = sinon.stub(); - requireModuleStub = sinon.stub(utils, 'requireModule'); + requireModuleStub = sinon.stub(utils, 'requireResolve'); requireModuleStub.withArgs('@angular/cli').returns(cliStub); getLogger = () => testInjector.logger; }); diff --git a/packages/karma-runner/test/unit/starters/stryker-karma.conf.spec.ts b/packages/karma-runner/test/unit/starters/stryker-karma.conf.spec.ts index a0b021c587..4a9b73e6e1 100644 --- a/packages/karma-runner/test/unit/starters/stryker-karma.conf.spec.ts +++ b/packages/karma-runner/test/unit/starters/stryker-karma.conf.spec.ts @@ -4,11 +4,11 @@ import { testInjector } from '@stryker-mutator/test-helpers'; import { expect } from 'chai'; import { Config, ConfigOptions, ClientOptions } from 'karma'; import * as sinon from 'sinon'; +import * as utils from '@stryker-mutator/util'; import sut = require('../../../src/starters/stryker-karma.conf'); import StrykerReporter from '../../../src/karma-plugins/stryker-reporter'; import TestHooksMiddleware, { TEST_HOOKS_FILE_NAME } from '../../../src/karma-plugins/test-hooks-middleware'; -import * as utils from '../../../src/utils'; describe('stryker-karma.conf.js', () => { let getLogger: sinon.SinonStub; @@ -19,7 +19,7 @@ describe('stryker-karma.conf.js', () => { config = new KarmaConfigMock(); getLogger = sinon.stub(); getLogger.returns(testInjector.logger); - requireModuleStub = sinon.stub(utils, 'requireModule'); + requireModuleStub = sinon.stub(utils, 'requireResolve'); sut.setGlobals({ getLogger, }); diff --git a/packages/util/.vscode/launch.json b/packages/util/.vscode/launch.json index 570048043d..5109194a91 100644 --- a/packages/util/.vscode/launch.json +++ b/packages/util/.vscode/launch.json @@ -7,14 +7,15 @@ { "type": "node", "request": "launch", - "name": "Unit tests", + "name": "Tests", "program": "${workspaceFolder}/../../node_modules/mocha/bin/_mocha", "args": [ "--timeout", "999999", "--colors", "${workspaceFolder}/test/helpers/**/*.js", - "${workspaceFolder}/test/unit/**/*.js" + "${workspaceFolder}/test/unit/**/*.js", + "${workspaceFolder}/test/integration/**/*.js" ], "internalConsoleOptions": "openOnSessionStart", "outFiles": [ @@ -41,4 +42,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/packages/util/package.json b/packages/util/package.json index 73a1f64a0e..7209f119fa 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -4,8 +4,10 @@ "description": "Contains utilities for Stryker, the mutation testing framework for JavaScript and friends", "main": "src/index.js", "scripts": { - "test": "nyc npm run test:unit", + "test": "nyc npm run test:all", + "test:all": "npm run test:unit && npm run test:integration", "test:unit": "mocha \"test/unit/**/*.js\"", + "test:integration": "mocha \"test/integration/**/*.js\"", "stryker": "node ../core/bin/stryker run" }, "repository": { diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index a81cd34c33..ed6d871b66 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -10,3 +10,4 @@ export * from './flat-map'; export * from './i'; export * from './task'; export * from './directory-require-cache'; +export * from './require-resolve'; diff --git a/packages/util/src/require-resolve.ts b/packages/util/src/require-resolve.ts new file mode 100644 index 0000000000..13664c3871 --- /dev/null +++ b/packages/util/src/require-resolve.ts @@ -0,0 +1,7 @@ +/** + * Require a module from the current working directory (or a different base dir) + * @see https://nodejs.org/api/modules.html#modules_require_resolve_paths_request + */ +export function requireResolve(id: string, from = process.cwd()): unknown { + return require(require.resolve(id, { paths: [from] })); +} diff --git a/packages/util/test/integration/require-resolve.it.spec.ts b/packages/util/test/integration/require-resolve.it.spec.ts new file mode 100644 index 0000000000..b04a303e61 --- /dev/null +++ b/packages/util/test/integration/require-resolve.it.spec.ts @@ -0,0 +1,26 @@ +import path = require('path'); + +import sinon = require('sinon'); +import { expect } from 'chai'; + +import { requireResolve } from '../../src'; + +const resolveTestResource: typeof path.resolve = path.resolve.bind(path, __dirname, '..', '..', 'testResources', 'require-resolve'); + +describe(requireResolve.name, () => { + it('should be able to require from parent', () => { + const bar = requireResolve('bar', resolveTestResource()); + expect(bar).eq('bar from parent'); + }); + + it('should be able to require from child', () => { + const bar = requireResolve('bar', resolveTestResource('foo')); + expect(bar).eq('bar from foo'); + }); + + it('should be able to require from current working directory', () => { + sinon.stub(process, 'cwd').returns(resolveTestResource('baz')); + const bar = requireResolve('bar'); + expect(bar).eq('bar from baz'); + }); +}); diff --git a/packages/util/testResources/.gitignore b/packages/util/testResources/.gitignore new file mode 100644 index 0000000000..736e8ae58a --- /dev/null +++ b/packages/util/testResources/.gitignore @@ -0,0 +1 @@ +!node_modules \ No newline at end of file diff --git a/packages/util/testResources/require-resolve/baz/node_modules/bar/index.js b/packages/util/testResources/require-resolve/baz/node_modules/bar/index.js new file mode 100644 index 0000000000..c36db21a16 --- /dev/null +++ b/packages/util/testResources/require-resolve/baz/node_modules/bar/index.js @@ -0,0 +1 @@ +module.exports = 'bar from baz'; diff --git a/packages/util/testResources/require-resolve/foo/node_modules/bar/index.js b/packages/util/testResources/require-resolve/foo/node_modules/bar/index.js new file mode 100644 index 0000000000..8404398c90 --- /dev/null +++ b/packages/util/testResources/require-resolve/foo/node_modules/bar/index.js @@ -0,0 +1 @@ +module.exports = 'bar from foo'; diff --git a/packages/util/testResources/require-resolve/node_modules/bar/index.js b/packages/util/testResources/require-resolve/node_modules/bar/index.js new file mode 100644 index 0000000000..ac217d1e6a --- /dev/null +++ b/packages/util/testResources/require-resolve/node_modules/bar/index.js @@ -0,0 +1 @@ +module.exports = 'bar from parent';