Repository: ember-cli/eslint-plugin-ember
Pull request #358 (2018-12-23 22:30:17)

Add 'assert-arg-order' rule.


Patch:
diff --git a/README.md b/README.md
index e86b86ea..d2eed8f4 100644
--- a/README.md
+++ b/README.md
@@ -103,6 +103,7 @@ The `--fix` option on the command line automatically fixes problems reported by
 
 |    | Rule ID | Description |
 |:---|:--------|:------------|
+|  | [assert-arg-order](./docs/rules/assert-arg-order.md) | Catch usages of Ember's `assert()` function that have the arguments passed in the wrong order. |
 | :white_check_mark: | [jquery-ember-run](./docs/rules/jquery-ember-run.md) | Prevents usage of jQuery without Ember Run Loop |
 | :white_check_mark: | [no-attrs-in-components](./docs/rules/no-attrs-in-components.md) | Disallow usage of this.attrs in components |
 | :white_check_mark: | [no-attrs-snapshot](./docs/rules/no-attrs-snapshot.md) | Disallow use of attrs snapshot in `didReceiveAttrs` and `didUpdateAttrs` |
diff --git a/docs/rules/assert-arg-order.md b/docs/rules/assert-arg-order.md
new file mode 100644
index 00000000..93d03d2f
--- /dev/null
+++ b/docs/rules/assert-arg-order.md
@@ -0,0 +1,29 @@
+# Catch usages of Ember&#39;s `assert()` function that have the arguments passed in the wrong order. (assert-arg-order)
+
+This rule aims to catch a common mistake when using Ember's `assert(String description, Boolean condition)` function. When writing an `assert`, the author may mistakenly pass the arguments in the reverse order, and not notice because the `assert` will pass with a truthy string as its second argument.
+
+## Rule Details
+
+Examples of **incorrect** code for this rule:
+
+```js
+import { assert } from '@ember/debug';
+
+...
+
+assert(label, 'Label must be present.');
+```
+
+Examples of **correct** code for this rule:
+
+```js
+import { assert } from '@ember/debug';
+
+...
+
+assert('Label must be present.', label);
+```
+
+## Further Reading
+
+* See the [documentation](https://www.emberjs.com/api/ember/release/functions/@ember%2Fdebug/assert) for the Ember `assert` function.
diff --git a/docs/rules/avoid-leaking-state-in-ember-objects.md b/docs/rules/avoid-leaking-state-in-ember-objects.md
index f0f45dd3..c124d55c 100644
--- a/docs/rules/avoid-leaking-state-in-ember-objects.md
+++ b/docs/rules/avoid-leaking-state-in-ember-objects.md
@@ -7,10 +7,7 @@
 Example configuration:
 
 ```
-const { DEFAULT_IGNORED_PROPERTIES } = require('eslint-plugin-ember/lib/rules/avoid-leaking-state-in-ember-objects');
-
 ember/avoid-leaking-state-in-ember-objects: [1, [
-  ...DEFAULT_IGNORED_PROPERTIES,
   'array',
   'of',
   'ignored',
diff --git a/lib/index.js b/lib/index.js
index 6f0bde72..31aec59e 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -4,6 +4,7 @@
 module.exports = {
   rules: {
     'alias-model-in-controller': require('./rules/alias-model-in-controller'),
+    'assert-arg-order': require('./rules/assert-arg-order'),
     'avoid-leaking-state-in-components': require('./rules/avoid-leaking-state-in-components'),
     'avoid-leaking-state-in-ember-objects': require('./rules/avoid-leaking-state-in-ember-objects'),
     'avoid-using-needs-in-controllers': require('./rules/avoid-using-needs-in-controllers'),
diff --git a/lib/recommended-rules.js b/lib/recommended-rules.js
index 73b42caa..bccf1d46 100644
--- a/lib/recommended-rules.js
+++ b/lib/recommended-rules.js
@@ -6,6 +6,7 @@
  */
 module.exports = {
   "ember/alias-model-in-controller": "off",
+  "ember/assert-arg-order": "off",
   "ember/avoid-leaking-state-in-components": "off",
   "ember/avoid-leaking-state-in-ember-objects": "error",
   "ember/avoid-using-needs-in-controllers": "error",
@@ -39,4 +40,4 @@ module.exports = {
   "ember/routes-segments-snake-case": "error",
   "ember/use-brace-expansion": "error",
   "ember/use-ember-get-and-set": "off"
-}
\ No newline at end of file
+}
diff --git a/lib/rules/assert-arg-order.js b/lib/rules/assert-arg-order.js
new file mode 100644
index 00000000..1f8d2c1f
--- /dev/null
+++ b/lib/rules/assert-arg-order.js
@@ -0,0 +1,52 @@
+'use strict';
+
+const utils = require('../utils/utils');
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+const ERROR_MESSAGE = "Usage of Ember's assert(String description, Boolean condition) function has its arguments passed in the wrong order.";
+
+module.exports = {
+  meta: {
+    docs: {
+      description: "Catch usages of Ember's `assert()` function that have the arguments passed in the wrong order.",
+      category: 'Possible Errors',
+      recommended: false
+    },
+    fixable: null,
+    ERROR_MESSAGE
+  },
+
+  create(context) {
+    return {
+      CallExpression(node) {
+        if (isAssertFunctionWithReversedArgs(node)) {
+          context.report({
+            node,
+            message: ERROR_MESSAGE
+          });
+        }
+      }
+    };
+  }
+};
+
+function isAssertFunctionWithReversedArgs(node) {
+  return (
+    isAssertFunction(node) &&
+    node.arguments.length === 2 &&
+    !isString(node.arguments[0]) &&
+    isString(node.arguments[1])
+  );
+}
+
+function isAssertFunction(node) {
+  const callee = node.callee;
+  return utils.isCallExpression(node) && utils.isIdentifier(callee) && callee.name === 'assert';
+}
+
+function isString(node) {
+  return utils.isTemplateLiteral(node) || (utils.isLiteral(node) && typeof node.value === 'string');
+}


Test patch:
diff --git a/tests/lib/rules/assert-arg-order.js b/tests/lib/rules/assert-arg-order.js
new file mode 100644
index 00000000..1526f843
--- /dev/null
+++ b/tests/lib/rules/assert-arg-order.js
@@ -0,0 +1,59 @@
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/assert-arg-order');
+const RuleTester = require('eslint').RuleTester;
+
+const { ERROR_MESSAGE } = rule;
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+  parserOptions: {
+    ecmaVersion: 2015,
+    sourceType: 'module'
+  }
+});
+ruleTester.run('assert-arg-order', rule, {
+  valid: [
+    {
+      code: "myFunction(true, 'My string.');"
+    },
+    {
+      code: "assert('My description.');"
+    },
+    {
+      code: "assert('My description.', true);"
+    },
+    {
+      code: "const CONDITION = true; assert('My description.', CONDITION);"
+    },
+    {
+      code: "const DESCRIPTION = 'description'; assert(DESCRIPTION, true);"
+    },
+    {
+      code: "const DESCRIPTION = 'description'; const CONDITION = true; assert(DESCRIPTION, CONDITION);"
+    }
+  ],
+  invalid: [
+    {
+      code: "assert(true, 'My description.');",
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }]
+    },
+    {
+      code: 'assert(true, `My ${123} description.`);', // eslint-disable-line no-template-curly-in-string
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }]
+    },
+    {
+      code: "const CONDITION = true; assert(CONDITION, 'My description.');",
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }]
+    },
+    {
+      code: 'const CONDITION = true; assert(CONDITION, `My ${123} description.`);', // eslint-disable-line no-template-curly-in-string
+      errors: [{ message: ERROR_MESSAGE, type: 'CallExpression' }]
+    }
+  ]
+});