Skip to content

Commit

Permalink
feat(rules)!: add tap-no-deprecated-aliases rule
Browse files Browse the repository at this point in the history
BREAKING CHANGE: deprecated `tap-consistent-assertions` rule

[email protected] deprecated the use of aliases for assertion methods, which
invalidates the concept of a "preferred" alias. This adds a separate
rule for enforcing the use of unaliased assertion methods. This rule
is auto-fixable.
  • Loading branch information
mdeltito committed Apr 12, 2021
1 parent 57425ff commit 436e120
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 38 deletions.
44 changes: 32 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,11 @@ the `eslint-plugin-` prefix. Then you can configure the rules you want to use:
"rules": {
"logdna/grouped-require": 2,
"logdna/require-file-extension": 2,
"logdna/tap-no-deprecated-aliases": 2,
"logdna/tap-consistent-assertions": [2, {
"preferredMap": {
"error": "error",
"equal": "strictEqual",
"not": "notStrictEqual",
"same": "deepEqual",
"notSame": "notDeepEqual",
"strictSame": "strictDeepEqual",
"strictNotSame": "strictDeepNotEqual",
"match": "match",
"notMatch": "notMatch",
"type": "type"
}
}]
}
Expand All @@ -50,7 +43,7 @@ the `eslint-plugin-` prefix. Then you can configure the rules you want to use:

### `logdna/grouped-require`

Enforce sorted require declarations within modules
> Enforce sorted require declarations within modules
```js
// Bad
Expand All @@ -73,9 +66,36 @@ const foo = require('./lib/foo.js') //local
* `typeOrder` [`<Array>`][] - sort order of require types
(default: `['static', 'builtin', 'contrib', 'scoped', 'local']`)

### `logdna/tap-consistent-assertions`
### `logdna/tap-no-deprecated-aliases`

Enforce consistent aliases for tap assertions
> Prevent usage of deprecated tap aliases (>= [email protected])
Tap deprecated assertion aliases as of version [`15.0.0`](https://node-tap.org/changelog/#150---2021-03-30).
This rule supersedes `logdna/tap-consistent-assertions` and will enforce the use of unaliased
assertion methods.

```js
// Bad
test('foo', async (t) => {
t.is_equal(1, 1)
t.strictEqual(1, 1)
t.identical(1, 1)
})

// Good
test('foo', async (t) => {
t.equal(1, 1)
t.equal(1, 1)
t.equal(1, 1)
})
```

#### Options
* `calleePattern` [`<String>`][] - pattern to match for tap's `Test` object (default: `/^t+$/`)

### [**Deprecated**] `logdna/tap-consistent-assertions`

> Enforce consistent aliases for tap assertions
```js
// {
Expand Down Expand Up @@ -112,7 +132,7 @@ test('foo', async (t) => {

### `logdna/require-file-extension`

Enforce file extension for local modules/files
> Enforce file extension for local modules/files
```js
// Bad
Expand Down
21 changes: 21 additions & 0 deletions lib/common/tap-assertion-selector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict'

module.exports = function tapAssertionSelector(targetMap, calleePattern) {
const matches = []
for (const method of targetMap.keys()) {
matches.push(`[callee.property.name="${method}"]`)
}

const scopeSelector = `:function[params.0.name=${calleePattern}]`
const calleeSelector = [
'[type="CallExpression"]'
, '[callee.type="MemberExpression"]'
, '[callee.computed=false]'
, '[callee.property.type="Identifier"]'
, `:matches(${matches.join(', ')})`
, '[callee.object.type="Identifier"]'
, `[callee.object.name=${calleePattern}]`
].join('')

return `${scopeSelector} ${calleeSelector}`
}
15 changes: 9 additions & 6 deletions lib/common/tap-synonyms.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ function multiword_(str) {
module.exports = multiword({
'ok': ['true', 'assert']
, 'notOk': ['false', 'assertNot']

, 'error': ['ifError', 'ifErr']
, 'throws': ['throw']
, 'doesNotThrow': ['notThrow']
Expand Down Expand Up @@ -74,16 +73,20 @@ module.exports = multiword({

// found has the fields in wanted, string matches regexp
, 'match': [
'has', 'hasFields', 'matches', 'similar', 'like', 'isLike'
, 'includes', 'include', 'isSimilar', 'contains'
'matches', 'similar', 'like', 'isLike', 'isSimilar'
]

, 'has': [
'hasFields', 'includes', 'include', 'contains'
]

, 'notMatch': [
'dissimilar', 'unsimilar', 'notSimilar', 'unlike', 'isUnlike'
, 'notLike', 'isNotLike', 'doesNotHave', 'isNotSimilar', 'isDissimilar'
]

, 'type': [
'isa', 'isA'
]
, 'type': ['isA']

// additional non-assertion aliases
, 'teardown': ['tearDown']
})
2 changes: 1 addition & 1 deletion lib/rules/grouped-require.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module.exports = {
meta: {
type: 'suggestion'
, docs: {
description: 'enforce grouped require declarations by scope'
description: 'Enforce grouped require declarations by scope'
, category: 'LogDNA'
}
, schema: [
Expand Down
1 change: 1 addition & 0 deletions lib/rules/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ module.exports = {
'grouped-require': require('./grouped-require.js')
, 'require-file-extension': require('./require-file-extension.js')
, 'tap-consistent-assertions': require('./tap-consistent-assertions.js')
, 'tap-no-deprecated-aliases': require('./tap-no-deprecated-aliases.js')
}
2 changes: 1 addition & 1 deletion lib/rules/require-file-extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module.exports = {
meta: {
type: 'suggestion'
, docs: {
description: 'enforce file extension for local modules/files'
description: 'Enforce file extension for local modules/files'
, category: 'LogDNA'
}
}
Expand Down
23 changes: 5 additions & 18 deletions lib/rules/tap-consistent-assertions.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
'use strict'

const synonyms = require('../common/tap-synonyms.js')
const tapAssertionSelector = require('../common/tap-assertion-selector.js')

const DEFAULT_CALLEE_PATTERN = '/^t+$/'

module.exports = {
meta: {
type: 'suggestion'
, fixable: 'code'
, deprecated: true
, docs: {
description: 'enforce consistent aliases for tap assertions'
description: 'Enforce consistent aliases for tap assertions'
, category: 'LogDNA'
}
, schema: [
Expand Down Expand Up @@ -51,24 +53,9 @@ module.exports = {
return {}
}

const targetSelectors = []
for (const method of targetMap.keys()) {
targetSelectors.push(`[callee.property.name="${method}"]`)
}

const scopeSelector = `:function[params.0.name=${calleePattern}]`
const calleeSelector = [
'[type="CallExpression"]'
, '[callee.type="MemberExpression"]'
, '[callee.computed=false]'
, '[callee.property.type="Identifier"]'
, `:matches(${targetSelectors.join(', ')})`
, '[callee.object.type="Identifier"]'
, `[callee.object.name=${calleePattern}]`
].join('')

const selector = tapAssertionSelector(targetMap, calleePattern)
return {
[`${scopeSelector} ${calleeSelector}`]: (node) => {
[selector]: (node) => {
const method = node.callee.property.name
const preferred = targetMap.get(method)

Expand Down
58 changes: 58 additions & 0 deletions lib/rules/tap-no-deprecated-aliases.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use strict'

const synonyms = require('../common/tap-synonyms.js')
const tapAssertionSelector = require('../common/tap-assertion-selector.js')

const DEFAULT_CALLEE_PATTERN = '/^t+$/'

module.exports = {
meta: {
type: 'suggestion'
, fixable: 'code'
, docs: {
description: 'Prevent usage of deprecated tap aliases (>= [email protected])'
, category: 'LogDNA'
}
, schema: [
{
type: 'object'
, properties: {
calleePattern: {
type: 'string'
}
}
, additionalProperties: false
}
]
}
, create(context) {
const {
calleePattern = DEFAULT_CALLEE_PATTERN
} = context.options[0] || {}

const targetMap = Object.keys(synonyms).reduce((map, primary) => {
for (const alias of synonyms[primary]) {
map.set(alias, primary)
}
return map
}, new Map())

const selector = tapAssertionSelector(targetMap, calleePattern)
return {
[selector]: (node) => {
const alias = node.callee.property.name
const main = targetMap.get(alias)

if (main && main !== alias) {
context.report({
node
, message: `The "${alias}" alias is deprecated in favor of "${main}"`
, fix(fixer) {
return fixer.replaceText(node.callee.property, main)
}
})
}
}
}
}
}
88 changes: 88 additions & 0 deletions test/lib/rules/tap-no-deprecated-aliases.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
'use strict'

const {test} = require('tap')
const {testRule} = require('../../common/bootstrap.js')
const rules = require('../../../lib/rules/index.js')

const RULE_NAME = 'tap-no-deprecated-aliases'

test(RULE_NAME, async (t) => {
testRule(t, RULE_NAME, rules[RULE_NAME], {
valid: [
{
code: `
test('foo', async (t) => {
t.same(true)
})
`
}
, {
code: `
test('foo', async (t) => {
t.match(true)
})
`
}
]
, invalid: [
{
code: `
test('foo', async (t) => {
t.deepEqual(true)
})
`
, output: `
test('foo', async (t) => {
t.same(true)
})
`
, errors: [
{message: 'The "deepEqual" alias is deprecated in favor of "same"'}
]
}
, {
code: `
test('foo', async (tt) => {
tt.loose_equal(true)
})
`
, output: `
test('foo', async (tt) => {
tt.same(true)
})
`
, errors: [
{message: 'The "loose_equal" alias is deprecated in favor of "same"'}
]
}
, {
code: `
test('foo', async (tea) => {
tea.tearDown(() => {
return
})
// unchanged - calleePattern does not match
t.deepEqual(true)
})
`
, output: `
test('foo', async (tea) => {
tea.teardown(() => {
return
})
// unchanged - calleePattern does not match
t.deepEqual(true)
})
`
, options: [{
calleePattern: '/^tea+$/'
}]
, errors: [
{message: 'The "tearDown" alias is deprecated in favor of "teardown"'}
]
}
]
})
})

0 comments on commit 436e120

Please sign in to comment.