Socket
Socket
Sign inDemoInstall

eslint-plugin-ava

Package Overview
Dependencies
Maintainers
2
Versions
59
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

eslint-plugin-ava - npm Package Compare versions

Comparing version 11.0.0 to 12.0.0

rules/.DS_Store

2

index.js

@@ -13,3 +13,3 @@ 'use strict';

parserOptions: {
ecmaVersion: 2020,
ecmaVersion: 2021,
sourceType: 'module'

@@ -16,0 +16,0 @@ },

{
"name": "eslint-plugin-ava",
"version": "11.0.0",
"version": "12.0.0",
"description": "ESLint rules for AVA",

@@ -36,27 +36,27 @@ "license": "MIT",

"eslint-utils": "^2.1.0",
"espree": "^7.2.0",
"espree": "^7.3.1",
"espurify": "^2.0.1",
"import-modules": "^2.0.0",
"import-modules": "^2.1.0",
"micro-spelling-correcter": "^1.1.1",
"pkg-dir": "^4.2.0",
"pkg-dir": "^5.0.0",
"resolve-from": "^5.0.0"
},
"devDependencies": {
"ava": "^3.11.1",
"ava": "^3.15.0",
"babel-eslint": "^10.1.0",
"c8": "^7.3.0",
"c8": "^7.6.0",
"chalk": "^4.1.0",
"del": "^5.1.0",
"eslint": "7.7.0",
"del": "^6.0.0",
"eslint": "^7.8.1",
"eslint-ava-rule-tester": "^4.0.0",
"eslint-plugin-eslint-plugin": "^2.3.0",
"execa": "^4.0.3",
"execa": "^5.0.0",
"listr": "^0.14.3",
"outdent": "^0.7.1",
"outdent": "^0.8.0",
"pify": "^5.0.0",
"tempy": "^0.6.0",
"xo": "^0.33.0"
"tempy": "^1.0.1",
"xo": "^0.38.2"
},
"peerDependencies": {
"eslint": ">=7.7.0"
"eslint": ">=7.22.0"
},

@@ -63,0 +63,0 @@ "ava": {

@@ -1,6 +0,6 @@

# eslint-plugin-ava [![Build Status](https://travis-ci.org/avajs/eslint-plugin-ava.svg?branch=master)](https://travis-ci.org/avajs/eslint-plugin-ava) [![Coverage Status](https://coveralls.io/repos/github/avajs/eslint-plugin-ava/badge.svg?branch=master)](https://coveralls.io/github/avajs/eslint-plugin-ava?branch=master)
# eslint-plugin-ava [![Coverage Status](https://coveralls.io/repos/github/avajs/eslint-plugin-ava/badge.svg?branch=main)](https://coveralls.io/github/avajs/eslint-plugin-ava?branch=main)
> ESLint rules for [AVA](https://ava.li)
> ESLint rules for [AVA](https://avajs.dev)
Translations: [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/related/eslint-plugin-ava/readme.md)
Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/related/eslint-plugin-ava/readme.md)

@@ -11,3 +11,2 @@ This plugin is bundled in [XO](https://github.com/xojs/xo). No need to do anything if you're using it.

## Install

@@ -19,3 +18,2 @@

## Usage

@@ -33,3 +31,3 @@

"parserOptions": {
"ecmaVersion": 2020,
"ecmaVersion": 2021,
"sourceType": "module"

@@ -80,3 +78,2 @@ },

## Rules

@@ -99,5 +96,5 @@

- [no-nested-tests](docs/rules/no-nested-tests.md) - Ensure no tests are nested.
- [no-only-test](docs/rules/no-only-test.md) - Ensure no `test.only()` are present. *(fixable)*
- [no-only-test](docs/rules/no-only-test.md) - Ensure no `test.only()` are present.
- [no-skip-assert](docs/rules/no-skip-assert.md) - Ensure no assertions are skipped.
- [no-skip-test](docs/rules/no-skip-test.md) - Ensure no tests are skipped. *(fixable)*
- [no-skip-test](docs/rules/no-skip-test.md) - Ensure no tests are skipped.
- [no-statement-after-end](docs/rules/no-statement-after-end.md) - Ensure `t.end()` is the last statement executed.

@@ -119,3 +116,2 @@ - [no-todo-implementation](docs/rules/no-todo-implementation.md) - Ensure `test.todo()` is not given an implementation function.

## Recommended config

@@ -122,0 +118,0 @@

'use strict';
const {visitIf} = require('enhance-visitors');
const {getStaticValue, isOpeningParenToken, isCommaToken} = require('eslint-utils');
const {getStaticValue, isOpeningParenToken, isCommaToken, findVariable} = require('eslint-utils');
const util = require('../util');

@@ -206,2 +206,9 @@ const createAvaRule = require('../create-ava-rule');

function isString(node) {
const {type} = node;
return type === 'TemplateLiteral' ||
type === 'TaggedTemplateExpression' ||
(type === 'Literal' && typeof node.value === 'string');
}
const create = context => {

@@ -275,2 +282,20 @@ const ava = createAvaRule();

}
if (gottenArgs === nArgs.max && nArgs.min !== nArgs.max) {
let lastArg = node.arguments[node.arguments.length - 1];
if (lastArg.type === 'Identifier') {
const variable = findVariable(context.getScope(), lastArg);
let value;
for (const ref of variable.references) {
value = ref.writeExpr || value;
}
lastArg = value;
}
if (!isString(lastArg)) {
report(node, 'Assertion message should be a string.');
}
}
})

@@ -277,0 +302,0 @@ });

@@ -92,3 +92,3 @@ 'use strict';

// TODO: Remove `.reduce()` usage.
// eslint-disable-next-line unicorn/no-reduce
// eslint-disable-next-line unicorn/no-array-reduce
const selectors = checks.reduce((result, check) => {

@@ -95,0 +95,0 @@ result[check.selector] = visitIf([

@@ -33,3 +33,3 @@ 'use strict';

// TODO: Remove `.reduce()` usage.
// eslint-disable-next-line unicorn/no-reduce
// eslint-disable-next-line unicorn/no-array-reduce
testModifiers.reduce((previous, current) => {

@@ -36,0 +36,0 @@ if (previous.name === current.name) {

@@ -19,9 +19,12 @@ 'use strict';

message: '`test.only()` should not be used.',
fix: fixer => {
return fixer.replaceTextRange.apply(null, util.removeTestModifier({
modifier: 'only',
node,
context
}));
}
suggest: [{
desc: 'Remove the `.only`',
fix: fixer => {
return fixer.replaceTextRange.apply(null, util.removeTestModifier({
modifier: 'only',
node,
context
}));
}
}]
});

@@ -28,0 +31,0 @@ }

@@ -19,9 +19,12 @@ 'use strict';

message: 'No tests should be skipped.',
fix: fixer => {
return fixer.replaceTextRange.apply(null, util.removeTestModifier({
modifier: 'skip',
node,
context
}));
}
suggest: [{
desc: 'Remove the `.skip`',
fix: fixer => {
return fixer.replaceTextRange.apply(null, util.removeTestModifier({
modifier: 'skip',
node,
context
}));
}
}]
});

@@ -28,0 +31,0 @@ }

@@ -24,6 +24,15 @@ 'use strict';

function pathStart() {
if (currentSegmentInfo !== undefined) {
segmentInfoStack.push(currentSegmentInfo);
currentSegmentInfo = undefined;
}
}
function pathEnd() {
currentSegmentInfo = segmentInfoStack.pop();
}
function segmentStart(segment) {
// A new CodePathSegment has started, create an "info" object to track this segments state.
segmentInfoStack.push(currentSegmentInfo);
currentSegmentInfo = {

@@ -38,7 +47,7 @@ ended: false,

function segmentEnd() {
currentSegmentInfo = segmentInfoStack.pop();
currentSegmentInfo = undefined;
}
function checkForEndExpression(node) {
if (isEndExpression(node)) {
if (isEndExpression(node) && currentSegmentInfo !== undefined) {
currentSegmentInfo.ended = true;

@@ -53,6 +62,10 @@ }

const ended = [currentSegmentInfo]
.concat(currentSegmentInfo.prev)
.filter(info => info.ended);
// If there is no current segment (this occurs in unreachable code), then we
// can't check whether `t.end()` was called
if (currentSegmentInfo === undefined) {
return;
}
const ended = [currentSegmentInfo, ...currentSegmentInfo.prev].filter(info => info.ended);
// If this segment or any previous segment is already ended, further statements are not allowed, report as an error.

@@ -91,2 +104,4 @@ if (ended.length > 0) {

},
onCodePathStart: pathStart,
onCodePathEnd: pathEnd,
onCodePathSegmentStart: segmentStart,

@@ -93,0 +108,0 @@ onCodePathSegmentEnd: segmentEnd,

@@ -17,3 +17,3 @@ 'use strict';

node,
message: '`test.todo()` should be not be used.'
message: '`test.todo()` should not be used.'
});

@@ -20,0 +20,0 @@ }

@@ -33,3 +33,3 @@ 'use strict';

if (unknown.length !== 0) {
if (unknown.length > 0) {
context.report({

@@ -36,0 +36,0 @@ node: unknown[0],

@@ -16,71 +16,182 @@ 'use strict';

const equalityTests = new Set([
'is',
'deepEqual'
]);
// Find the latest reference to the given identifier's name.
const findReference = name => {
const reference = context.getScope().references.find(reference => reference.identifier.name === name);
const definitions = reference.resolved.defs;
return definitions[definitions.length - 1].node;
};
return ava.merge({
CallExpression: visitIf([
ava.isInTestFile,
ava.isInTestNode
])(node => {
// Call a boolean assertion, for example, `t.true`, `t.false`, …
const isBooleanAssertion = node.callee.type === 'MemberExpression' &&
booleanTests.has(node.callee.property.name) &&
util.getNameOfRootNodeObject(node.callee) === 't';
if (reference && reference.resolved) {
const definitions = reference.resolved.defs;
if (!isBooleanAssertion) {
if (definitions.length === 0) {
return;
}
const firstArg = node.arguments[0];
return definitions[definitions.length - 1].node;
}
};
// First argument is a call expression
const isFunctionCall = firstArg.type === 'CallExpression';
if (!isFunctionCall || !firstArg.callee.property) {
return;
}
/*
Recursively find the "origin" node of the given node.
const {name} = firstArg.callee.property;
let lookup = {};
let variable = {};
Note: `context.getScope()` doesn't contain information about the outer scope so in most cases this function will only find the reference directly above the current scope. So the following code will only find the reference in this order: y -> x, and it will have no knowledge of the number `0`. (assuming we run this function on the identifier `y`)
if (name === 'test') {
// `lookup.test(variable)`
lookup = firstArg.callee.object;
variable = firstArg.arguments[0];
} else if (['search', 'match'].includes(name)) {
// `variable.match(lookup)`
lookup = firstArg.arguments[0];
variable = firstArg.callee.object;
}
```
const test = require('ava');
let isRegExp = lookup.regex;
let x = 0;
let y = x;
// It's not a regexp but an identifier
if (!isRegExp && lookup.type === 'Identifier') {
const reference = findReference(lookup.name);
isRegExp = reference.init.regex;
test(t => {
t.is(y, 0);
});
```
*/
const findRootReference = node => {
if (node.type === 'Identifier') {
const reference = findReference(node.name);
if (reference && reference.init) {
return findRootReference(reference.init);
}
if (!isRegExp) {
return node;
}
if (node.type === 'CallExpression' || node.type === 'NewExpression') {
return findRootReference(node.callee);
}
if (node.type === 'MemberExpression') {
return findRootReference(node.object);
}
return node;
};
/*
Determine if the given node is a regex expression.
There are two ways to create regex expressions in JavaScript: Regex literal and `RegExp` class.
1. Regex literal can be easily looked up using the `.regex` property on the node.
2. `RegExp` class can't be looked up so the function just checks for the name `RegExp`.
*/
const isRegExp = lookup => {
if (lookup.regex) {
return true;
}
// Look up references in case it's a variable or RegExp declaration.
const reference = findRootReference(lookup);
return reference.regex || reference.name === 'RegExp';
};
const booleanHandler = node => {
const firstArg = node.arguments[0];
const isFunctionCall = firstArg.type === 'CallExpression';
if (!isFunctionCall || !firstArg.callee.property) {
return;
}
const {name} = firstArg.callee.property;
let lookup = {};
let variable = {};
if (name === 'test') {
// Represent: `lookup.test(variable)`
lookup = firstArg.callee.object;
variable = firstArg.arguments[0];
} else if (['search', 'match'].includes(name)) {
// Represent: `variable.match(lookup)`
lookup = firstArg.arguments[0];
variable = firstArg.callee.object;
}
if (!isRegExp(lookup)) {
return;
}
const assertion = ['true', 'truthy'].includes(node.callee.property.name) ? 'regex' : 'notRegex';
const fix = fixer => {
const source = context.getSourceCode();
return [
fixer.replaceText(node.callee.property, assertion),
fixer.replaceText(firstArg, `${source.getText(variable)}, ${source.getText(lookup)}`)
];
};
context.report({
node,
message: `Prefer using the \`t.${assertion}()\` assertion.`,
fix
});
};
const equalityHandler = node => {
const [firstArg, secondArg] = node.arguments;
const firstArgumentIsRegex = isRegExp(firstArg);
const secondArgumentIsRegex = isRegExp(secondArg);
// If both are regex, or neither are, the expression is ok.
if (firstArgumentIsRegex === secondArgumentIsRegex) {
return;
}
const matchee = secondArgumentIsRegex ? firstArg : secondArg;
const regex = secondArgumentIsRegex ? secondArg : firstArg;
const booleanFixer = assertion => fixer => {
const source = context.getSourceCode();
return [
fixer.replaceText(node.callee.property, assertion),
fixer.replaceText(firstArg, `${source.getText(regex.arguments[0])}`),
fixer.replaceText(secondArg, `${source.getText(regex.callee.object)}`)
];
};
// Only fix a statically verifiable equality.
if (regex && matchee.type === 'Literal') {
let assertion;
if (matchee.raw === 'true') {
assertion = 'regex';
} else if (matchee.raw === 'false') {
assertion = 'notRegex';
} else {
return;
}
const assertion = ['true', 'truthy'].includes(node.callee.property.name) ? 'regex' : 'notRegex';
const fix = fixer => {
const source = context.getSourceCode();
return [
fixer.replaceText(node.callee.property, assertion),
fixer.replaceText(firstArg, `${source.getText(variable)}, ${source.getText(lookup)}`)
];
};
context.report({
node,
message: `Prefer using the \`t.${assertion}()\` assertion.`,
fix
fix: booleanFixer(assertion)
});
}
};
return ava.merge({
CallExpression: visitIf([
ava.isInTestFile,
ava.isInTestNode
])(node => {
const isAssertion = node.callee.type === 'MemberExpression' &&
util.getNameOfRootNodeObject(node.callee) === 't';
const isBooleanAssertion = isAssertion &&
booleanTests.has(node.callee.property.name);
const isEqualityAssertion = isAssertion &&
equalityTests.has(node.callee.property.name);
if (isBooleanAssertion) {
booleanHandler(node);
} else if (isEqualityAssertion) {
equalityHandler(node);
}
})

@@ -87,0 +198,0 @@ });

@@ -15,3 +15,3 @@ 'use strict';

])(node => {
const firstArgumentIsFunction = node.arguments.length < 1 || util.isFunctionExpression(node.arguments[0]);
const firstArgumentIsFunction = node.arguments.length === 0 || util.isFunctionExpression(node.arguments[0]);

@@ -18,0 +18,0 @@ if (firstArgumentIsFunction) {

@@ -23,3 +23,3 @@ 'use strict';

if (node.object.type === 'MemberExpression') {
return getMemberNodes(node.object).concat(node.property);
return [...getMemberNodes(node.object), node.property];
}

@@ -26,0 +26,0 @@

@@ -51,3 +51,3 @@ 'use strict';

if (node.type === 'MemberExpression') {
return getTestModifiers(node.object).concat(node.property);
return [...getTestModifiers(node.object), node.property];
}

@@ -83,3 +83,3 @@

if (node.object.type === 'MemberExpression') {
return getMembers(node.object).concat(name);
return [...getMembers(node.object), name];
}

@@ -130,2 +130,2 @@

exports.assertionMethods = new Set(assertionMethodNames);
exports.executionMethods = new Set(assertionMethodNames.concat(['end', 'plan', 'log', 'teardown', 'timeout']));
exports.executionMethods = new Set([...assertionMethodNames, 'end', 'plan', 'log', 'teardown', 'timeout']);
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc