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

Eslint check for mocking node core modules #786

Merged
merged 5 commits into from
Jun 11, 2020
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
17 changes: 17 additions & 0 deletions .eslintrc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
root: true
parser: "@typescript-eslint/parser"
plugins:
- "@typescript-eslint"
- zowe-explorer
env:
node: true
es6: true
rules:
zowe-explorer/no-unmocked-core-modules:
- error
- coreModuleNames:
- fs
dkelosky marked this conversation as resolved.
Show resolved Hide resolved
filePathPattern: ".*\\.unit\\.test\\..*"
parserOptions:
sourceType: module
48 changes: 48 additions & 0 deletions eslint-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# eslint-plugin-zowe-explorer

Custom ESLint Rules for ZOWE Explorer

## Installation

You'll first need to install [ESLint](http://eslint.org):

```
$ npm i eslint --save-dev
VitGottwald marked this conversation as resolved.
Show resolved Hide resolved
```

Next, install `eslint-plugin-zowe-explorer`:

```
$ npm install eslint-plugin-zowe-explorer --save-dev
```

**Note:** If you installed ESLint globally (using the `-g` flag) then you must also install `eslint-plugin-zowe-explorer` globally.

## Usage

Add `zowe-explorer` to the plugins section of your `.eslintrc` configuration file. You can omit the `eslint-plugin-` prefix:

```json
{
"plugins": ["zowe-explorer"]
}
```

Then configure the rules you want to use under the rules section.
For example to disallow the use of unmocked `fs` module in unit tests
(whose file name contains `.unit.test.`) specify this rule config

```json
{
"rules": {
"zowe-explorer/no-unmocked-core-modules": [
"error",
{ "coreModuleNames": ["fs"], "filePathPattern": ".*\\.unit\\.test\\..*" }
]
}
}
```

## Supported Rules

- no-unmocked-core-modules
22 changes: 22 additions & 0 deletions eslint-plugin/docs/rules/no-unmocked-core-modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Disallow the use of unmocked node core module (no-unmocked-core-modules)

Node modules are [automatically mocked by jest](https://jestjs.io/docs/en/manual-mocks#mocking-node-modules). That means that if a file exists in the `__mocks__` directory next to `node_modules`, the mock will be returned instead of the actual module on a `require/import`.
VitGottwald marked this conversation as resolved.
Show resolved Hide resolved

However, node core modules (fs, path, ...) are not automatically mocked and an explicit

```js
jest.mock("fs");
```

is required to get a mock instead of the actual module.

## Rule details

If your unit tests mock or modify some of the core modules, you may want to make sure `jest.mock` is called for them so that the actual core module is not modified.

This rule will report an error if a module is imported and `jest.mock` is not called for it.

## Options

- `coreModuleNames` an array with the names of core modules that should be always mocked
- `filePathPattern` of the unit tests for which this rule should be mandeted as a string, for example `".*\\.unit\\.test\\..*"`
1 change: 1 addition & 0 deletions eslint-plugin/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = { testMatch: ["**/tests/**/*.js"] };
13 changes: 13 additions & 0 deletions eslint-plugin/lib/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @fileoverview Custom ESLint Rules for ZOWE Explorer
*/
"use strict";

var requireIndex = require("requireindex");

//------------------------------------------------------------------------------
// Plugin Definition
//------------------------------------------------------------------------------

// import all rules in lib/rules
module.exports.rules = requireIndex(__dirname + "/rules");
78 changes: 78 additions & 0 deletions eslint-plugin/lib/rules/no-unmocked-core-modules.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
const isJestMockOf = (name) => (node) => {
return (
node.type === "ExpressionStatement" &&
node.expression.type === "CallExpression" &&
node.expression.callee.type === "MemberExpression" &&
node.expression.callee.object.type === "Identifier" &&
node.expression.callee.object.name === "jest" &&
node.expression.callee.property.type === "Identifier" &&
node.expression.callee.property.name === "mock" &&
node.expression.arguments.length === 1 &&
node.expression.arguments[0].type === "Literal" &&
node.expression.arguments[0].value === name
);
};

module.exports = {
meta: {
type: "problem",
docs: {
description:
"disallow the use of unmocked node core modules in unit tests",
category: "Unit Tests",
recommended: true,
url: "",
},
schema: [
{
type: "object",
properties: {
coreModuleNames: {
type: "array",
items: { type: "string" },
default: ["fs"],
},
filePathPattern: {
type: "string",
default: ".*\\.test\\..*",
},
},
additionalProperties: false,
},
],
fixable: "code",
},
create: (context) => {
const {
coreModuleNames = ["fs"],
filePathPattern = /.*\.ttest\..*/,
} = context.options[0];
const fileName = context.getFilename();
if (!fileName.match(RegExp(filePathPattern))) {
// file does not match pattern
return {}; // no linting
}

return {
ImportDeclaration(node) {
const moduleName = node.source.value;
if (
coreModuleNames.includes(moduleName) && // imports node core module
node.parent.type === "Program" &&
!node.parent.body.some(isJestMockOf(moduleName)) // does not mock it
) {
context.report({
node,
message: `Use jest.mock('${moduleName}') to mock a node core module`,
fix: (fixer) =>
fixer.insertTextAfter(
node,
`
jest.mock('${moduleName}');`
),
});
}
},
};
},
};
Loading