Repository: unexpectedjs/unexpected
Pull request #519 (2018-09-28 21:55:41)

Fix/hack inspection of arrow functions with leading newline (prettier-ism)
After adopting prettier in unexpected-mitm, we've run into some functions in the test suite where the indentation isn't properly normalized when inspected by the `function` type.

This fix/hack allows us to fix these cases: https://github.com/unexpectedjs/unexpected-mitm/pull/66/files


Patch:
diff --git a/lib/types.js b/lib/types.js
index 132942b0..dc9eca58 100644
--- a/lib/types.js
+++ b/lib/types.js
@@ -971,6 +971,7 @@ module.exports = function(expect) {
         let openingBrace;
         let isWrappedInBraces = true;
         let closingBrace = '}';
+        let reindentBodyLevel = 0;
         if (matchBodyAndIndent) {
           openingBrace = matchBodyAndIndent[1];
           body = matchBodyAndIndent[2];
@@ -986,7 +987,20 @@ module.exports = function(expect) {
             openingBrace = matchBodyAndIndent[1];
             isWrappedInBraces = false;
             body = matchBodyAndIndent[2];
-            bodyIndent = matchBodyAndIndent[3] || '';
+            const matchInitialNewline = openingBrace.match(/^\n( +)/);
+            if (matchInitialNewline) {
+              // An arrow function whose body starts with a newline, as prettier likes to output, eg.:
+              //        () =>
+              //          foo(
+              //            1
+              //          );
+              // Shuffle/hack things around so it will be formatted correctly:
+              openingBrace = '\n';
+              bodyIndent = matchInitialNewline[1];
+              reindentBodyLevel = 1;
+            } else {
+              bodyIndent = matchBodyAndIndent[3] || '';
+            }
             closingBrace = '';
           }
         }
@@ -996,11 +1010,12 @@ module.exports = function(expect) {
           body = body.replace(new RegExp(`^ {${bodyIndent.length}}`, 'mg'), '');
           const indent = detectIndent(body);
           body = body.replace(
-            new RegExp(`^(?:${indent.indent})+`, 'mg'),
+            new RegExp(`^(?:${indent.indent})*`, 'mg'),
             ({ length }) =>
               utils.leftPad(
                 '',
-                (length / indent.amount) * output.indentationWidth,
+                (length / indent.amount + reindentBodyLevel) *
+                  output.indentationWidth,
                 ' '
               )
           );


Test patch:
diff --git a/test/types/function-type.spec.js b/test/types/function-type.spec.js
index d869ac94..dc1a2f1a 100644
--- a/test/types/function-type.spec.js
+++ b/test/types/function-type.spec.js
@@ -218,6 +218,82 @@ describe('function type', function() {
     });
   }
 
+  // We can't complete this test if the runtime doesn't support arrow functions:
+  var arrowFunctionWith1SpaceIndentAndLeadingNewline;
+  try {
+    // eslint-disable-next-line no-new-func
+    arrowFunctionWith1SpaceIndentAndLeadingNewline = new Function(
+      'return () =>\n foo(\n  1\n )'
+    )();
+  } catch (e) {}
+
+  if (arrowFunctionWith1SpaceIndentAndLeadingNewline) {
+    it('should reindent an implicit return multiline arrow function', function() {
+      expect(
+        arrowFunctionWith1SpaceIndentAndLeadingNewline,
+        'to inspect as',
+        '() =>\n  foo(\n    1\n  )'
+      );
+    });
+  }
+
+  // We can't complete this test if the runtime doesn't support arrow functions:
+  var arrowFunctionWith2SpaceIndentAndLeadingNewline;
+  try {
+    // eslint-disable-next-line no-new-func
+    arrowFunctionWith2SpaceIndentAndLeadingNewline = new Function(
+      'return () =>\n        foo(\n          1\n        )'
+    )();
+  } catch (e) {}
+
+  if (arrowFunctionWith2SpaceIndentAndLeadingNewline) {
+    it('should reindent an implicit return multiline arrow function', function() {
+      expect(
+        arrowFunctionWith2SpaceIndentAndLeadingNewline,
+        'to inspect as',
+        '() =>\n  foo(\n    1\n  )'
+      );
+    });
+  }
+
+  // We can't complete this test if the runtime doesn't support arrow functions:
+  var arrowFunctionWith3SpaceIndentAndLeadingNewline;
+  try {
+    // eslint-disable-next-line no-new-func
+    arrowFunctionWith3SpaceIndentAndLeadingNewline = new Function(
+      'return () =>\n      foo(\n         1\n      )'
+    )();
+  } catch (e) {}
+
+  if (arrowFunctionWith3SpaceIndentAndLeadingNewline) {
+    it('should reindent an implicit return multiline arrow function with 4 space indent', function() {
+      expect(
+        arrowFunctionWith3SpaceIndentAndLeadingNewline,
+        'to inspect as',
+        '() =>\n  foo(\n    1\n  )'
+      );
+    });
+  }
+
+  // We can't complete this test if the runtime doesn't support arrow functions:
+  var arrowFunctionWith4SpaceIndentAndLeadingNewline;
+  try {
+    // eslint-disable-next-line no-new-func
+    arrowFunctionWith4SpaceIndentAndLeadingNewline = new Function(
+      'return () =>\n        foo(\n            1\n        )'
+    )();
+  } catch (e) {}
+
+  if (arrowFunctionWith4SpaceIndentAndLeadingNewline) {
+    it('should reindent an implicit return multiline arrow function with 4 space indent', function() {
+      expect(
+        arrowFunctionWith4SpaceIndentAndLeadingNewline,
+        'to inspect as',
+        '() =>\n  foo(\n    1\n  )'
+      );
+    });
+  }
+
   // We can't complete this test if the runtime doesn't support arrow functions:
   var multiParamArrowFunction;
   try {