Skip to content

chore: lint and format on pre-commit and ci #216

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

Merged
merged 2 commits into from
Aug 17, 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
2 changes: 1 addition & 1 deletion .huskyrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"hooks": {
"pre-commit": "npm run test && lint-staged",
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
8 changes: 6 additions & 2 deletions .lintstagedrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"*.js": ["eslint --fix", "git add"],
"*.md": ["prettier --write", "git add"]
"*.{js,ts}": [
"eslint --fix",
"prettier --write",
"jest --findRelatedTests",
],
"*.md": ["prettier --write"]
}
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ before_script:

jobs:
include:
- stage: validation
node_js: 14
env: ESLINT=6
script:
- npm run format:check
- npm run lint -- --max-warnings 0
- stage: release
if: branch = master AND type != pull_request
node_js: 14
Expand Down
40 changes: 30 additions & 10 deletions lib/node-utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/experimental-utils';
import {
AST_NODE_TYPES,
TSESTree,
} from '@typescript-eslint/experimental-utils';
import { RuleContext } from '@typescript-eslint/experimental-utils/dist/ts-eslint';

export function isCallExpression(
Expand Down Expand Up @@ -96,8 +99,10 @@ export function findClosestCallNode(
}
}

export function isObjectExpression(node: TSESTree.Expression): node is TSESTree.ObjectExpression {
return node?.type === AST_NODE_TYPES.ObjectExpression
export function isObjectExpression(
node: TSESTree.Expression
): node is TSESTree.ObjectExpression {
return node?.type === AST_NODE_TYPES.ObjectExpression;
}

export function hasThenProperty(node: TSESTree.Node) {
Expand All @@ -114,16 +119,24 @@ export function isAwaitExpression(
return node && node.type === AST_NODE_TYPES.AwaitExpression;
}

export function isArrowFunctionExpression(node: TSESTree.Node): node is TSESTree.ArrowFunctionExpression {
return node && node.type === AST_NODE_TYPES.ArrowFunctionExpression
export function isArrowFunctionExpression(
node: TSESTree.Node
): node is TSESTree.ArrowFunctionExpression {
return node && node.type === AST_NODE_TYPES.ArrowFunctionExpression;
}

export function isReturnStatement(node: TSESTree.Node): node is TSESTree.ReturnStatement {
return node && node.type === AST_NODE_TYPES.ReturnStatement
export function isReturnStatement(
node: TSESTree.Node
): node is TSESTree.ReturnStatement {
return node && node.type === AST_NODE_TYPES.ReturnStatement;
}

export function isAwaited(node: TSESTree.Node) {
return isAwaitExpression(node) || isArrowFunctionExpression(node) || isReturnStatement(node)
return (
isAwaitExpression(node) ||
isArrowFunctionExpression(node) ||
isReturnStatement(node)
);
}

export function isPromiseResolved(node: TSESTree.Node) {
Expand All @@ -138,8 +151,15 @@ export function isPromiseResolved(node: TSESTree.Node) {
return hasThenProperty(parent);
}

export function getVariableReferences(context: RuleContext<string, []>, node: TSESTree.Node) {
return (isVariableDeclarator(node) && context.getDeclaredVariables(node)[0].references.slice(1)) || [];
export function getVariableReferences(
context: RuleContext<string, []>,
node: TSESTree.Node
) {
return (
(isVariableDeclarator(node) &&
context.getDeclaredVariables(node)[0].references.slice(1)) ||
[]
);
}

export function isRenderFunction(
Expand Down
40 changes: 22 additions & 18 deletions lib/rules/no-multiple-assertions-wait-for.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils'
import { getDocsUrl } from '../utils'
import { isBlockStatement, findClosestCallNode, isMemberExpression, isCallExpression, isIdentifier } from '../node-utils'
import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils';
import { getDocsUrl } from '../utils';
import {
isBlockStatement,
isMemberExpression,
isCallExpression,
isIdentifier,
} from '../node-utils';

export const RULE_NAME = 'no-multiple-assertions-wait-for';

const WAIT_EXPRESSION_QUERY =
'CallExpression[callee.name=/^(waitFor)$/]';
const WAIT_EXPRESSION_QUERY = 'CallExpression[callee.name=/^(waitFor)$/]';

export type MessageIds = 'noMultipleAssertionWaitFor';
type Options = [];
Expand All @@ -15,36 +19,36 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
meta: {
type: 'suggestion',
docs: {
description:
"It's preferred to avoid multiple assertions in `waitFor`",
description: "It's preferred to avoid multiple assertions in `waitFor`",
category: 'Best Practices',
recommended: false,
},
messages: {
noMultipleAssertionWaitFor: 'Avoid using multiple assertions within `waitFor` callback',
noMultipleAssertionWaitFor:
'Avoid using multiple assertions within `waitFor` callback',
},
fixable: null,
schema: [],
},
defaultOptions: [],
create: function(context) {
function reportMultipleAssertion(
node: TSESTree.BlockStatement
) {
function reportMultipleAssertion(node: TSESTree.BlockStatement) {
const totalExpect = (body: Array<TSESTree.Node>): Array<TSESTree.Node> =>
body.filter((node: TSESTree.ExpressionStatement) => {
if (
isCallExpression(node.expression) &&
isMemberExpression(node.expression.callee) &&
isCallExpression(node.expression.callee.object)
) {
const object: TSESTree.CallExpression = node.expression.callee.object
const expressionName: string = isIdentifier(object.callee) && object.callee.name
return expressionName === 'expect'
const object: TSESTree.CallExpression =
node.expression.callee.object;
const expressionName: string =
isIdentifier(object.callee) && object.callee.name;
return expressionName === 'expect';
} else {
return false
return false;
}
})
});

if (isBlockStatement(node) && totalExpect(node.body).length > 1) {
context.report({
Expand All @@ -59,5 +63,5 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
[`${WAIT_EXPRESSION_QUERY} > ArrowFunctionExpression > BlockStatement`]: reportMultipleAssertion,
[`${WAIT_EXPRESSION_QUERY} > FunctionExpression > BlockStatement`]: reportMultipleAssertion,
};
}
})
},
});
4 changes: 3 additions & 1 deletion lib/rules/no-node-access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({
let isImportingTestingLibrary = false;

function checkTestingEnvironment(node: TSESTree.ImportDeclaration) {
isImportingTestingLibrary = /testing-library/g.test(node.source.value as string);
isImportingTestingLibrary = /testing-library/g.test(
node.source.value as string
);
}

function showErrorForNodeAccess(node: TSESTree.MemberExpression) {
Expand Down
48 changes: 27 additions & 21 deletions lib/rules/no-side-effects-wait-for.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils'
import { getDocsUrl, hasTestingLibraryImportModule } from '../utils'
import { isBlockStatement, findClosestCallNode, isMemberExpression, isCallExpression, isIdentifier } from '../node-utils'
import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils';
import { getDocsUrl, hasTestingLibraryImportModule } from '../utils';
import {
isBlockStatement,
isMemberExpression,
isCallExpression,
isIdentifier,
} from '../node-utils';

export const RULE_NAME = 'no-side-effects-wait-for';

const WAIT_EXPRESSION_QUERY =
'CallExpression[callee.name=/^(waitFor)$/]';
const WAIT_EXPRESSION_QUERY = 'CallExpression[callee.name=/^(waitFor)$/]';

const SIDE_EFFECTS: Array<string> = ['fireEvent', 'userEvent']
const SIDE_EFFECTS: Array<string> = ['fireEvent', 'userEvent'];

export type MessageIds = 'noSideEffectsWaitFor';
type Options = [];
Expand All @@ -17,13 +21,13 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
meta: {
type: 'suggestion',
docs: {
description:
"It's preferred to avoid side effects in `waitFor`",
description: "It's preferred to avoid side effects in `waitFor`",
category: 'Best Practices',
recommended: false,
},
messages: {
noSideEffectsWaitFor: 'Avoid using side effects within `waitFor` callback',
noSideEffectsWaitFor:
'Avoid using side effects within `waitFor` callback',
},
fixable: null,
schema: [],
Expand All @@ -32,25 +36,27 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
create: function(context) {
let isImportingTestingLibrary = false;

function reportSideEffects(
node: TSESTree.BlockStatement
) {
function reportSideEffects(node: TSESTree.BlockStatement) {
const hasSideEffects = (body: Array<TSESTree.Node>): boolean =>
body.some((node: TSESTree.ExpressionStatement) => {
if (
isCallExpression(node.expression) &&
isMemberExpression(node.expression.callee) &&
isIdentifier(node.expression.callee.object)
) {
const object: TSESTree.Identifier = node.expression.callee.object
const identifierName: string = object.name
return SIDE_EFFECTS.includes(identifierName)
const object: TSESTree.Identifier = node.expression.callee.object;
const identifierName: string = object.name;
return SIDE_EFFECTS.includes(identifierName);
} else {
return false
return false;
}
})
});

if (isImportingTestingLibrary && isBlockStatement(node) && hasSideEffects(node.body)) {
if (
isImportingTestingLibrary &&
isBlockStatement(node) &&
hasSideEffects(node.body)
) {
context.report({
node,
loc: node.loc.start,
Expand All @@ -64,7 +70,7 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
[`${WAIT_EXPRESSION_QUERY} > FunctionExpression > BlockStatement`]: reportSideEffects,
ImportDeclaration(node: TSESTree.ImportDeclaration) {
isImportingTestingLibrary = hasTestingLibraryImportModule(node);
}
},
};
}
})
},
});
33 changes: 22 additions & 11 deletions lib/rules/prefer-screen-queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,23 @@ export const RULE_NAME = 'prefer-screen-queries';
export type MessageIds = 'preferScreenQueries';
type Options = [];

const ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING = ['container', 'baseElement']
const ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING = [
'container',
'baseElement',
];
const ALL_QUERIES_COMBINATIONS_REGEXP = ALL_QUERIES_COMBINATIONS.join('|');

function usesContainerOrBaseElement(node: TSESTree.CallExpression) {
const secondArgument = node.arguments[1]
return isObjectExpression(secondArgument) && secondArgument.properties.some((property) => isProperty(property) && isIdentifier(property.key) && ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING.includes(property.key.name))
const secondArgument = node.arguments[1];
return (
isObjectExpression(secondArgument) &&
secondArgument.properties.some(
property =>
isProperty(property) &&
isIdentifier(property.key) &&
ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING.includes(property.key.name)
)
);
}

export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
Expand Down Expand Up @@ -57,15 +68,14 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({

return {
VariableDeclarator(node) {
if (!
isCallExpression(node.init) || !
isIdentifier(node.init.callee) ) {
return
if (!isCallExpression(node.init) || !isIdentifier(node.init.callee)) {
return;
}
const isWithinFunction =
node.init.callee.name === 'within';
const isWithinFunction = node.init.callee.name === 'within';
// TODO add the custom render option #198
const usesRenderOptions = node.init.callee.name === 'render' && usesContainerOrBaseElement(node.init);
const usesRenderOptions =
node.init.callee.name === 'render' &&
usesContainerOrBaseElement(node.init);

if (!isWithinFunction && !usesRenderOptions) {
return;
Expand Down Expand Up @@ -117,7 +127,8 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
isCallExpression(node.parent.object) &&
isIdentifier(node.parent.object.callee) &&
node.parent.object.callee.name !== 'within' &&
node.parent.object.callee.name === 'render' && !usesContainerOrBaseElement(node.parent.object)
node.parent.object.callee.name === 'render' &&
!usesContainerOrBaseElement(node.parent.object)
) {
reportInvalidUsage(node);
return;
Expand Down
Loading