Repository: vuejs/eslint-plugin-vue
Pull request #573 (2018-09-14 08:49:32)

Adding default array example of correct rule


Patch:
diff --git a/README.md b/README.md
index 4ad4a204..2cebaac5 100644
--- a/README.md
+++ b/README.md
@@ -284,8 +284,9 @@ The `vue-eslint-parser` uses the parser which is set by `parserOptions.parser` t
   ```
 
 2. Make sure your tool is set to lint `.vue` files.
-  - CLI targets only `.js` files by default. You have to specify additional extensions by `--ext` option or glob patterns. E.g. `eslint "src/**/*.{js,vue}"` or `eslint src --ext .vue`.
+  - CLI targets only `.js` files by default. You have to specify additional extensions by `--ext` option or glob patterns. E.g. `eslint "src/**/*.{js,vue}"` or `eslint src --ext .vue`. If you use `@vue/cli-plugin-eslint` and the `vue-cli-service lint` command - you don't have to worry about it.
   - VSCode targets only JavaScript or HTML files by default. You have to add `"vue"` to the `"eslint.validate"` array in vscode settings. e.g. `"eslint.validate": [ "javascript", "javascriptreact", "vue" ]`
+  - If you use `Vetur` plugin in VSCode - set `"vetur.validation.template": false` to avoid default Vetur template validation. Check out [vetur documentation](https://github.com/vuejs/vetur/blob/master/docs/linting-error.md) for more info.
 
 ## :anchor: Semantic Versioning Policy
 
diff --git a/docs/rules/no-unused-components.md b/docs/rules/no-unused-components.md
index 6375c111..28e6ac32 100644
--- a/docs/rules/no-unused-components.md
+++ b/docs/rules/no-unused-components.md
@@ -72,7 +72,7 @@ Note that components registered under other than `PascalCase` name have to be ca
 }
 ```
 
-- `ignoreWhenBindingPresent` ... surpresses all errors if binding has been detected in the template
+- `ignoreWhenBindingPresent` ... suppresses all errors if binding has been detected in the template
     default `true`
 
 
diff --git a/docs/rules/require-valid-default-prop.md b/docs/rules/require-valid-default-prop.md
index 2049534b..eee9692c 100644
--- a/docs/rules/require-valid-default-prop.md
+++ b/docs/rules/require-valid-default-prop.md
@@ -49,10 +49,16 @@ props: {
   // object/array defaults should be returned from a factory function
   propE: {
     type: Object,
-    default: function () {
+    default() {
       return { message: 'hello' }
     }
-  }
+  },
+  propF: {
+    type: Array,
+    default() {
+      return []
+    }
+  }  
 }
 ```
 
diff --git a/docs/rules/valid-v-on.md b/docs/rules/valid-v-on.md
index 18580ccc..02bc8ba9 100644
--- a/docs/rules/valid-v-on.md
+++ b/docs/rules/valid-v-on.md
@@ -36,7 +36,24 @@ This rule does not check syntax errors in directives because it's checked by [no
 
 ## :wrench: Options
 
-Nothing.
+This rule has an object option:
+
+`"modifiers"`: [] (default) array of additional allowed modifiers.
+
+### Example:
+
+```json
+"vue/valid-v-on": [2, {
+  modifiers: ['foo']
+}]
+```
+
+:+1: Examples of **correct** code for this rule:
+
+```html
+<div @click.foo="foo"/>
+<div v-on:click.foo="foo"/>
+```
 
 ## :couple: Related rules
 
diff --git a/lib/rules/html-self-closing.js b/lib/rules/html-self-closing.js
index 65aac0a5..de310743 100644
--- a/lib/rules/html-self-closing.js
+++ b/lib/rules/html-self-closing.js
@@ -165,7 +165,11 @@ module.exports = {
               if (elementType === ELEMENT_TYPE.VOID) {
                 return fixer.replaceText(close, '>')
               }
-              return fixer.replaceText(close, `></${node.rawName}>`)
+              // If only `close` is targeted for replacement, it conflicts with `component-name-in-template-casing`,
+              // so replace the entire element.
+              // return fixer.replaceText(close, `></${node.rawName}>`)
+              const elementPart = sourceCode.text.slice(node.range[0], close.range[0])
+              return fixer.replaceText(node, elementPart + `></${node.rawName}>`)
             }
           })
         }
diff --git a/lib/rules/order-in-components.js b/lib/rules/order-in-components.js
index ffd103eb..c9529495 100644
--- a/lib/rules/order-in-components.js
+++ b/lib/rules/order-in-components.js
@@ -193,22 +193,22 @@ module.exports = {
               if (hasSideEffectsPossibility) {
                 return undefined
               }
-              const comma = sourceCode.getTokenAfter(propertyNode)
-              const hasAfterComma = isComma(comma)
+              const afterComma = sourceCode.getTokenAfter(propertyNode)
+              const hasAfterComma = isComma(afterComma)
 
-              const codeStart = sourceCode.getTokenBefore(propertyNode).range[1] // to include comments
-              const codeEnd = hasAfterComma ? comma.range[1] : propertyNode.range[1]
+              const beforeComma = sourceCode.getTokenBefore(propertyNode)
+              const codeStart = beforeComma.range[1] // to include comments
+              const codeEnd = hasAfterComma ? afterComma.range[1] : propertyNode.range[1]
 
               const propertyCode = sourceCode.text.slice(codeStart, codeEnd) + (hasAfterComma ? '' : ',')
               const insertTarget = sourceCode.getTokenBefore(firstUnorderedPropertyNode)
-              // If we can upgrade requirements to `eslint@>4.1.0`, this code can be replaced by:
-              // return [
-              //   fixer.removeRange([codeStart, codeEnd]),
-              //   fixer.insertTextAfter(insertTarget, propertyCode)
-              // ]
-              const insertStart = insertTarget.range[1]
-              const newCode = propertyCode + sourceCode.text.slice(insertStart, codeStart)
-              return fixer.replaceTextRange([insertStart, codeEnd], newCode)
+
+              const removeStart = hasAfterComma ? codeStart : beforeComma.range[0]
+
+              return [
+                fixer.removeRange([removeStart, codeEnd]),
+                fixer.insertTextAfter(insertTarget, propertyCode)
+              ]
             }
           })
         }
diff --git a/lib/rules/valid-v-on.js b/lib/rules/valid-v-on.js
index 203a2ae3..13073d9a 100644
--- a/lib/rules/valid-v-on.js
+++ b/lib/rules/valid-v-on.js
@@ -93,7 +93,7 @@ const KEY_ALIASES = new Set([
   'media-previous-track', 'power', 'unidentified'
 ])
 
-function isValidModifier (modifier) {
+function isValidModifier (modifier, customModifiers) {
   return (
     // built-in aliases
     VALID_MODIFIERS.has(modifier) ||
@@ -102,7 +102,9 @@ function isValidModifier (modifier) {
     // keyAlias (an Unicode character)
     Array.from(modifier).length === 1 ||
     // keyAlias (special keys)
-    KEY_ALIASES.has(modifier)
+    KEY_ALIASES.has(modifier) ||
+    // custom modifiers
+    customModifiers.has(modifier)
   )
 }
 
@@ -118,14 +120,27 @@ module.exports = {
       url: 'https://github.com/vuejs/eslint-plugin-vue/blob/v5.0.0-beta.3/docs/rules/valid-v-on.md'
     },
     fixable: null,
-    schema: []
+    schema: [
+      {
+        type: 'object',
+        properties: {
+          modifiers: {
+            type: 'array'
+          }
+        },
+        additionalProperties: false
+      }
+    ]
   },
 
   create (context) {
+    const options = context.options[0] || {}
+    const customModifiers = new Set(options.modifiers || [])
+
     return utils.defineTemplateBodyVisitor(context, {
       "VAttribute[directive=true][key.name='on']" (node) {
         for (const modifier of node.key.modifiers) {
-          if (!isValidModifier(modifier)) {
+          if (!isValidModifier(modifier, customModifiers)) {
             context.report({
               node,
               loc: node.loc,
diff --git a/lib/utils/indent-common.js b/lib/utils/indent-common.js
index a4dee6f2..c8c86725 100644
--- a/lib/utils/indent-common.js
+++ b/lib/utils/indent-common.js
@@ -18,7 +18,6 @@ const KNOWN_NODES = new Set(['ArrayExpression', 'ArrayPattern', 'ArrowFunctionEx
 const LT_CHAR = /[\r\n\u2028\u2029]/
 const LINES = /[^\r\n\u2028\u2029]+(?:$|\r\n|[\r\n\u2028\u2029])/g
 const BLOCK_COMMENT_PREFIX = /^\s*\*/
-const TRIVIAL_PUNCTUATOR = /^[(){}[\],;]$/
 
 /**
  * Normalize options.
@@ -245,21 +244,6 @@ function isClosingToken (token) {
   )
 }
 
-/**
- * Check whether a given token is trivial or not.
- * @param {Token} token The token to check.
- * @returns {boolean} `true` if the token is trivial.
- */
-function isTrivialToken (token) {
-  return token != null && (
-    (token.type === 'Punctuator' && TRIVIAL_PUNCTUATOR.test(token.value)) ||
-    token.type === 'HTMLTagOpen' ||
-    token.type === 'HTMLEndTagOpen' ||
-    token.type === 'HTMLTagClose' ||
-    token.type === 'HTMLSelfClosingTagClose'
-  )
-}
-
 /**
  * Creates AST event handlers for html-indent.
  *
@@ -564,24 +548,22 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
   /**
    * Calculate correct indentation of the line of the given tokens.
    * @param {Token[]} tokens Tokens which are on the same line.
-   * @returns {number} Correct indentation. If it failed to calculate then `Number.MAX_SAFE_INTEGER`.
+   * @returns {object|null} Correct indentation. If it failed to calculate then `null`.
    */
-  function getExpectedIndent (tokens) {
-    const trivial = isTrivialToken(tokens[0])
-    let expectedIndent = Number.MAX_SAFE_INTEGER
+  function getExpectedIndents (tokens) {
+    const expectedIndents = []
 
     for (let i = 0; i < tokens.length; ++i) {
       const token = tokens[i]
       const offsetInfo = offsets.get(token)
 
-      // If the first token is not trivial then ignore trivial following tokens.
-      if (offsetInfo != null && (trivial || !isTrivialToken(token))) {
+      if (offsetInfo != null) {
         if (offsetInfo.expectedIndent != null) {
-          expectedIndent = Math.min(expectedIndent, offsetInfo.expectedIndent)
+          expectedIndents.push(offsetInfo.expectedIndent)
         } else {
           const baseOffsetInfo = offsets.get(offsetInfo.baseToken)
           if (baseOffsetInfo != null && baseOffsetInfo.expectedIndent != null && (i === 0 || !baseOffsetInfo.baseline)) {
-            expectedIndent = Math.min(expectedIndent, baseOffsetInfo.expectedIndent + offsetInfo.offset * options.indentSize)
+            expectedIndents.push(baseOffsetInfo.expectedIndent + offsetInfo.offset * options.indentSize)
             if (baseOffsetInfo.baseline) {
               break
             }
@@ -589,8 +571,14 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
         }
       }
     }
+    if (!expectedIndents.length) {
+      return null
+    }
 
-    return expectedIndent
+    return {
+      expectedIndent: expectedIndents[0],
+      expectedBaseIndent: expectedIndents.reduce((a, b) => Math.min(a, b))
+    }
   }
 
   /**
@@ -746,11 +734,14 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
     // Calculate and save expected indentation.
     const firstToken = tokens[0]
     const actualIndent = firstToken.loc.start.column
-    const expectedIndent = getExpectedIndent(tokens)
-    if (expectedIndent === Number.MAX_SAFE_INTEGER) {
+    const expectedIndents = getExpectedIndents(tokens)
+    if (!expectedIndents) {
       return
     }
 
+    const expectedBaseIndent = expectedIndents.expectedBaseIndent
+    const expectedIndent = expectedIndents.expectedIndent
+
     // Debug log
     // console.log('line', firstToken.loc.start.line, '=', { actualIndent, expectedIndent }, 'from:')
     // for (const token of tokens) {
@@ -773,11 +764,11 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
         if (offsetInfo.baseline) {
           // This is a baseline token, so the expected indent is the column of this token.
           if (options.indentChar === ' ') {
-            offsetInfo.expectedIndent = Math.max(0, token.loc.start.column + expectedIndent - actualIndent)
+            offsetInfo.expectedIndent = Math.max(0, token.loc.start.column + expectedBaseIndent - actualIndent)
           } else {
             // In hard-tabs mode, it cannot align tokens strictly, so use one additional offset.
             // But the additional offset isn't needed if it's at the beginning of the line.
-            offsetInfo.expectedIndent = expectedIndent + (token === tokens[0] ? 0 : 1)
+            offsetInfo.expectedIndent = expectedBaseIndent + (token === tokens[0] ? 0 : 1)
           }
           baseline.add(token)
         } else if (baseline.has(offsetInfo.baseToken)) {
@@ -786,7 +777,7 @@ module.exports.defineVisitor = function create (context, tokenStore, defaultOpti
           baseline.add(token)
         } else {
           // Otherwise, set the expected indent of this line.
-          offsetInfo.expectedIndent = expectedIndent
+          offsetInfo.expectedIndent = expectedBaseIndent
         }
       }
     }


Test patch:
diff --git a/tests/fixtures/script-indent/array-expression-03.vue b/tests/fixtures/script-indent/array-expression-03.vue
new file mode 100644
index 00000000..0e15d8aa
--- /dev/null
+++ b/tests/fixtures/script-indent/array-expression-03.vue
@@ -0,0 +1,6 @@
+<!--{}-->
+<script>
+var v = [() =>
+           1
+         ,b]
+</script>
\ No newline at end of file
diff --git a/tests/fixtures/script-indent/array-expression-04.vue b/tests/fixtures/script-indent/array-expression-04.vue
new file mode 100644
index 00000000..1530c24b
--- /dev/null
+++ b/tests/fixtures/script-indent/array-expression-04.vue
@@ -0,0 +1,6 @@
+<!--{}-->
+<script>
+var v = [() =>
+  1
+,]
+</script>
\ No newline at end of file
diff --git a/tests/fixtures/script-indent/indent-valid-fixture-01.vue b/tests/fixtures/script-indent/indent-valid-fixture-01.vue
new file mode 100644
index 00000000..c1423bf9
--- /dev/null
+++ b/tests/fixtures/script-indent/indent-valid-fixture-01.vue
@@ -0,0 +1,541 @@
+<!--{"options":[2,{"switchCase":1}]}-->
+<!-- see https://github.com/eslint/eslint/blob/master/tests/fixtures/rules/indent/indent-valid-fixture-1.js -->
+<script>
+/* ESLint setting reference value
+eslint indent:[2,2,{
+"SwitchCase":1,
+"VariableDeclarator":{"var":2,"let":2},
+"FunctionExpression":{"parameters":"first"},
+"CallExpression":{"arguments":"first"},
+}]*/
+if (a) {
+  var b = c;
+  var d = e
+    * f;
+  var e = f; // <-
+  // ->
+  function g() {
+    if (h) {
+      var i = j;
+    } // <-
+  } // <-
+
+  while (k) l++;
+  while (m) {
+    n--; // ->
+  } // <-
+
+  do {
+    o = p +
+      q;
+    o = p +
+      q;
+  } while(r); // <-
+
+  for (var s in t) {
+    u++;
+  }
+
+  for (;;) {
+    v++; // <-
+  }
+
+  if ( w ) {
+    x++;
+  } else if (y) {
+    z++; // <-
+    aa++;
+  } else { // <-
+    bb++; // ->
+  } // ->
+}
+
+/**/var b; // NO ERROR: single line multi-line comments followed by code is OK
+/*
+*
+*/ var b; // NO ERROR: multi-line comments followed by code is OK
+
+var arr = [
+  a,
+  b,
+  c,
+  function (){
+    d
+  }, // <-
+  {},
+  {
+    a: b,
+    c: d,
+    d: e
+  },
+  [
+    f,
+    g,
+    h,
+    i
+  ],
+  [j]
+];
+
+var obj = {
+  a: {
+    b: {
+      c: d,
+      e: f,
+      g: h +
+        i
+    }
+  },
+  g: [
+    h,
+    i,
+    j,
+    k
+  ]
+};
+
+var arrObject = {a:[
+  a,
+  b,
+  c
+]};
+
+var objArray = [{
+  a: b,
+  b: c,
+  c: d
+}];
+
+var arrArray = [[
+  a,
+  b,
+  c
+]];
+
+var objObject = {a:{
+  a: b,
+  b: c,
+  c: d
+}};
+
+
+switch (a) {
+  case 'a':
+    var a = 'b'; // ->
+    break;
+  case 'b':
+    var a = 'b';
+    break;
+  case 'c':
+    var a = 'b'; // <-
+    break;
+  case 'd':
+    var a = 'b';
+    break; // ->
+  case 'f':
+    var a = 'b';
+    break;
+  case 'g':     {
+    var a = 'b';
+    break;
+  }
+  case 'z':
+  default:
+    break; // <-
+}
+
+a.b('hi')
+  .c(a.b()) // <-
+  .d(); // <-
+
+if ( a ) {
+  if ( b ) {
+    d.e(f) // ->
+      .g() // ->
+      .h(); // ->
+
+    i.j(m)
+      .k()
+      .l();
+
+    n.o(p) // <-
+      .q() // <-
+      .r(); // <-
+  }
+}
+
+var a = b,
+    c = function () {
+      h = i; // ->
+      j = k;
+      l = m; // <-
+    },
+    e = {
+      f: g,
+      n: o,
+      p: q
+    },
+    r = [
+      s,
+      t,
+      u
+    ];
+
+var a = function () {
+  b = c; // ->
+  d = e;
+  f = g; // <-
+};
+
+function c1(a, b) {
+  if (a || (a &&
+    b)) {
+    return d;
+  }
+}
+
+if ( a
+  || b ) {
+  var x; // ->
+  var c,
+      d = function(a,
+                   b) { // <-
+                     a; // -> // Indent it specially with `vue/script-indent`.
+                     b;
+                     c; // <-
+                   }
+}
+
+
+a({
+  d: 1
+});
+
+a(
+  1
+);
+
+a(
+  b({
+    d: 1
+  })
+);
+
+a(
+  b(
+    c({
+      d: 1,
+      e: 1,
+      f: 1
+    })
+  )
+);
+
+a({ d: 1 });
+
+aa(
+  b({
+    c: d, // ->
+    e: f,
+    f: g
+  }) // ->
+);
+
+aaaaaa(
+  b,
+  c,
+  {
+    d: a
+  }
+);
+
+a(b, c,
+  d, e,
+  f, g
+);  // <-
+
+a(
+); // <-
+
+aaaaaa(
+  b,
+  c, {
+    d: a
+  }, {
+    e: f
+  }
+);
+
+a.b()
+  .c(function(){
+    var a;
+  }).d.e;
+
+if (a == 'b') {
+  if (c && d) e = f
+  else g('h').i('j')
+}
+
+a = function (b, c) {
+  return a(function () {
+    var d = e
+    var f = g
+    var h = i
+
+    if (!j) k('l', (m = n))
+    if (o) p
+    else if (q) r
+  })
+}
+
+var a = function() {
+  "b"
+    .replace(/a/, "a")
+    .replace(/bc?/, function(e) {
+      return "b" + (e.f === 2 ? "c" : "f");
+    })
+    .replace(/d/, "d");
+};
+
+$(b)
+  .on('a', 'b', function() { $(c).e('f'); })
+  .on('g', 'h', function() { $(i).j('k'); });
+
+a
+  .b('c',
+     'd');
+
+a
+  .b('c', [ 'd', function(e) {
+    e++;
+  }]);
+
+var a = function() {
+      a++;
+      b++; // <-
+      c++; // <-
+    },
+    b;
+
+var b = [
+      a,
+      b,
+      c
+    ],
+    c;
+
+var c = {
+      a: 1,
+      b: 2,
+      c: 3
+    },
+    d;
+
+// holes in arrays indentation
+x = [
+  1,
+  1,
+  1,
+  1,
+  1,
+  1,
+  1,
+  1,
+  1,
+  1
+];
+
+try {
+  a++;
+  b++; // <-
+  c++; // ->
+} catch (d) {
+  e++;
+  f++; // <-
+  g++; // ->
+} finally {
+  h++;
+  i++; // <-
+  j++; // ->
+}
+
+if (array.some(function(){
+  return true;
+})) {
+  a++; // ->
+  b++;
+  c++; // <-
+}
+
+var a = b.c(function() {
+      d++;
+    }),
+    e;
+
+switch (true) {
+  case (a
+    && b):
+  case (c // ->
+    && d):
+  case (e // <-
+    && f):
+  case (g
+    && h):
+    var i = j; // <-
+    var k = l;
+    var m = n; // ->
+}
+
+if (a) {
+  b();
+}
+else {
+  c(); // ->
+  d();
+  e(); // <-
+}
+
+if (a) b();
+else {
+  c(); // ->
+  d();
+  e(); // <-
+}
+
+if (a) {
+  b();
+} else c();
+
+if (a) {
+  b();
+}
+else c();
+
+a();
+
+if( "very very long multi line" +
+  "with weird indentation" ) {
+  b();
+  a(); // ->
+  c(); // <-
+}
+
+a( "very very long multi line" +
+  "with weird indentation", function() {
+  b();
+  a(); // ->
+  c(); // <-
+}); // <-
+
+a = function(content, dom) {
+  b();
+  c(); // <-
+  d(); // ->
+};
+
+a = function(content, dom) {
+  b();
+  c(); // <-
+  d(); // ->
+};
+
+a = function(content, dom) {
+  b(); // ->
+};
+
+a = function(content, dom) {
+  b(); // ->
+};
+
+a('This is a terribly long description youll ' +
+  'have to read', function () {
+  b(); // <-
+  c(); // <-
+}); // <-
+
+if (
+  array.some(function(){
+    return true;
+  })
+) {
+  a++; // ->
+  b++;
+  c++; // <-
+}
+
+function c2(d) {
+  return {
+    e: function(f, g) {
+    }
+  };
+}
+
+function a1(b) {
+  switch(x) {
+    case 1:
+      if (foo) {
+        return 5;
+      }
+  }
+}
+
+function a2(b) {
+  switch(x) {
+    case 1:
+      c;
+  }
+}
+
+function a3(b) {
+  switch(x) {
+    case 1: c;
+  }
+}
+
+function test() {
+  var a = 1;
+  {
+    a();
+  }
+}
+
+{
+  a();
+}
+
+function a4(b) {
+  switch(x) {
+    case 1:
+      { // <-
+        a(); // ->
+      }
+      break;
+    default:
+    {
+      b();
+    }
+  }
+}
+
+switch (a) {
+  default:
+    if (b)
+      c();
+}
+
+function test1(x) {
+  switch (x) {
+    case 1:
+      return function() {
+        var a = 5;
+        return a;
+      };
+  }
+}
+
+switch (a) {
+  default:
+    if (b)
+      c();
+}
+</script>
\ No newline at end of file
diff --git a/tests/fixtures/script-indent/issue441.vue b/tests/fixtures/script-indent/issue441.vue
new file mode 100644
index 00000000..a6738f2a
--- /dev/null
+++ b/tests/fixtures/script-indent/issue441.vue
@@ -0,0 +1,10 @@
+<!--{}-->
+<script>
+o
+  .fn(() => {
+  });
+
+o2
+  .fn(() => {
+  });fn()
+</script>
\ No newline at end of file
diff --git a/tests/fixtures/script-indent/issue443.vue b/tests/fixtures/script-indent/issue443.vue
new file mode 100644
index 00000000..4a8c356e
--- /dev/null
+++ b/tests/fixtures/script-indent/issue443.vue
@@ -0,0 +1,27 @@
+<!--{}-->
+<script>
+let v = [
+  o
+    .fn(
+    ),
+]
+
+let v2 = [
+  o2
+    .fn(
+    ),e2
+]
+
+var v3 = [() =>
+            1
+          ,o3
+            .fn(
+            ),e3]
+
+var v4 = [() =>
+            1
+          ,o4
+            .fn(
+            ),e4
+]
+</script>
\ No newline at end of file
diff --git a/tests/fixtures/script-indent/sequence-expression-04.vue b/tests/fixtures/script-indent/sequence-expression-04.vue
new file mode 100644
index 00000000..f263bf52
--- /dev/null
+++ b/tests/fixtures/script-indent/sequence-expression-04.vue
@@ -0,0 +1,8 @@
+<!--{}-->
+<script>
+(
+  a +
+    b,2,
+  3
+)
+</script>
diff --git a/tests/lib/autofix.js b/tests/lib/autofix.js
new file mode 100644
index 00000000..5a38dd0e
--- /dev/null
+++ b/tests/lib/autofix.js
@@ -0,0 +1,160 @@
+/**
+ * @author Yosuke Ota <https://github.com/ota-meshi>
+ * See LICENSE file in root directory for full license.
+ */
+'use strict'
+
+const Linter = require('eslint').Linter
+const chai = require('chai')
+
+const rules = require('../..').rules
+
+const assert = chai.assert
+
+const baseConfig = {
+  parser: 'vue-eslint-parser',
+  parserOptions: {
+    ecmaVersion: 2018,
+    sourceType: 'module',
+    ecmaFeatures: {
+      jsx: true
+    }
+  }
+}
+
+describe('Complex autofix test cases', () => {
+  const linter = new Linter()
+  for (const key of Object.keys(rules)) {
+    const ruleId = `vue/${key}`
+    linter.defineRule(ruleId, rules[key])
+  }
+
+  // https://github.com/vuejs/eslint-plugin-vue/issues/566
+  describe('Autofix of `vue/order-in-components` and `comma-dangle` should not conflict.', () => {
+    const config = Object.assign({}, baseConfig, { 'rules': {
+      'vue/order-in-components': ['error'],
+      'comma-dangle': ['error', 'always']
+    }})
+
+    it('Even if set comma-dangle:always, the output should be as expected.', () => {
+      const code = `
+      <script>
+        export default {
+          data() {
+          },
+          name: 'burger'
+        };
+      </script>`
+      const output = `
+      <script>
+        export default {
+          name: 'burger',
+          data() {
+          },
+        };
+      </script>`
+      assert.equal(
+        linter.verifyAndFix(code, config, 'test.vue').output,
+        output
+      )
+    })
+
+    it('Even if include comments, the output should be as expected.', () => {
+      const code = `
+      <script>
+        export default {
+          /**data*/
+          data() {
+          }/*after data*/,
+          /**name*/
+          name: 'burger'/*after name*/
+        };
+      </script>`
+      const output = `
+      <script>
+        export default {
+          /**name*/
+          name: 'burger',
+          /**data*/
+          data() {
+          },/*after data*//*after name*/
+        };
+      </script>`
+      assert.equal(
+        linter.verifyAndFix(code, config, 'test.vue').output,
+        output
+      )
+    })
+  })
+
+  // https://github.com/vuejs/eslint-plugin-vue/issues/554
+  describe('Autofix of `html-self-closing` and `component-name-in-template-casing` should not conflict.', () => {
+    const kebabConfig = Object.assign({}, baseConfig, { 'rules': {
+      'vue/html-self-closing': ['error', {
+        'html': {
+          'component': 'never'
+        }
+      }],
+      'vue/component-name-in-template-casing': ['error', 'kebab-case']
+    }})
+
+    const pascalConfig = Object.assign({}, baseConfig, { 'rules': {
+      'vue/html-self-closing': ['error', {
+        'html': {
+          'component': 'never'
+        }
+      }],
+      'vue/component-name-in-template-casing': ['error']
+    }})
+
+    it('Even if set kebab-case, the output should be as expected.', () => {
+      const code = `
+      <template>
+        <VueComponent />
+      </template>`
+      const output = `
+      <template>
+        <vue-component ></vue-component>
+      </template>`
+
+      assert.equal(
+        linter.verifyAndFix(code, kebabConfig, 'test.vue').output,
+        output
+      )
+    })
+
+    it('Even if set PascalCase, the output should be as expected.', () => {
+      const code = `
+      <template>
+        <vue-component />
+      </template>`
+      const output = `
+      <template>
+        <VueComponent ></VueComponent>
+      </template>`
+
+      assert.equal(
+        linter.verifyAndFix(code, pascalConfig, 'test.vue').output,
+        output
+      )
+    })
+
+    it('Even if element have an attributes, the output should be as expected.', () => {
+      const code = `
+      <template>
+        <vue-component attr
+          id="item1" />
+      </template>`
+      const output = `
+      <template>
+        <VueComponent attr
+          id="item1" ></VueComponent>
+      </template>`
+
+      assert.equal(
+        linter.verifyAndFix(code, pascalConfig, 'test.vue').output,
+        output
+      )
+    })
+  })
+})
diff --git a/tests/lib/rules/order-in-components.js b/tests/lib/rules/order-in-components.js
index 236524e8..b022508f 100644
--- a/tests/lib/rules/order-in-components.js
+++ b/tests/lib/rules/order-in-components.js
@@ -374,7 +374,7 @@ ruleTester.run('order-in-components', rule, {
           data() {
           },
           test: 'ok',
-          name: 'burger',
+          name: 'burger'
         };
       `,
       options: [{ order: ['data', 'test', 'name'] }],
@@ -401,7 +401,33 @@ ruleTester.run('order-in-components', rule, {
           name: 'burger',
           /** data provider */
           data() {
-          },
+          }
+        };
+      `,
+      errors: [{
+        message: 'The "name" property should be above the "data" property on line 4.',
+        line: 7
+      }]
+    },
+    {
+      filename: 'example.vue',
+      code: `
+        export default {
+          /** data provider */
+          data() {
+          }/*test*/,
+          /** name of vue component */
+          name: 'burger'
+        };
+      `,
+      parserOptions,
+      output: `
+        export default {
+          /** name of vue component */
+          name: 'burger',
+          /** data provider */
+          data() {
+          }/*test*/
         };
       `,
       errors: [{
@@ -413,7 +439,7 @@ ruleTester.run('order-in-components', rule, {
       filename: 'example.vue',
       code: `export default {data(){},name:'burger'};`,
       parserOptions,
-      output: `export default {name:'burger',data(){},};`,
+      output: `export default {name:'burger',data(){}};`,
       errors: [{
         message: 'The "name" property should be above the "data" property on line 1.',
         line: 1
diff --git a/tests/lib/rules/valid-v-on.js b/tests/lib/rules/valid-v-on.js
index 3c02be86..f24e10dc 100644
--- a/tests/lib/rules/valid-v-on.js
+++ b/tests/lib/rules/valid-v-on.js
@@ -86,6 +86,16 @@ tester.run('valid-v-on', rule, {
     {
       filename: 'test.vue',
       code: '<template><div v-on="{a, b, c: d}"></div></template>'
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div @keydown.bar="foo"></div></template>',
+      options: [{ modifiers: ['bar'] }]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div v-on:keydown.bar.aaa="foo"></div></template>',
+      options: [{ modifiers: ['bar', 'aaa'] }]
     }
   ],
   invalid: [
@@ -103,6 +113,18 @@ tester.run('valid-v-on', rule, {
       filename: 'test.vue',
       code: '<template><div @click></div></template>',
       errors: ["'v-on' directives require that attribute value or verb modifiers."]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div @keydown.bar.aaa="foo"></div></template>',
+      errors: ["'v-on' directives don't support the modifier 'aaa'."],
+      options: [{ modifiers: ['bar'] }]
+    },
+    {
+      filename: 'test.vue',
+      code: '<template><div @keydown.bar.aaa="foo"></div></template>',
+      errors: ["'v-on' directives don't support the modifier 'bar'."],
+      options: [{ modifiers: ['aaa'] }]
     }
   ]
 })