Repository: jlongster/prettier
Pull request #161 (2017-01-13 06:34:12)

Add test options for each of the options
This PR updates `run_spec` to support options and adds tests for each option of the API

jlongster: Very interesting. I hadn't thought about this technique, I was going to advertise that options were basically untested and it's up to the community to fix bugs. But this is a pretty cool idea. I don't think we need to extensively test every option because some options might want to be tested across a majority of JS features, but haven't a few tests would be good.

Will look through it more, thanks!

Patch:
diff --git a/bin/prettier.js b/bin/prettier.js
index a5b6229f0..f034c7e3c 100755
--- a/bin/prettier.js
+++ b/bin/prettier.js
@@ -33,6 +33,13 @@ if (!filenames.length && !stdin) {
   process.exit(1);
 }
 
+function formatWithShebang(input) {
+  const index = input.indexOf("\n");
+  const shebang = input.slice(0, index + 1);
+  const programInput = input.slice(index + 1);
+  return shebang + format(programInput);
+}
+
 function format(input) {
   return jscodefmt.format(input, {
     printWidth: argv["print-width"],
@@ -70,7 +77,7 @@ if (stdin) {
 
       let output;
       try {
-        output = format(input);
+        output = input.startsWith("#!") ? formatWithShebang(input) : format(input);
       } catch (e) {
         process.exitCode = 2;
         console.error(e);
diff --git a/src/printer.js b/src/printer.js
index b121e7d51..fd335cb76 100644
--- a/src/printer.js
+++ b/src/printer.js
@@ -465,40 +465,48 @@ function genericPrintNoParens(path, options, print) {
     }
 
     if (n.specifiers && n.specifiers.length > 0) {
-      var foundImportSpecifier = false;
+      var standalones = [];
+      var grouped = [];
 
-      path.each(
-        function(specifierPath) {
-          var i = specifierPath.getName();
-
-          if (i > 0) {
-            parts.push(", ");
-          }
-
-          var value = specifierPath.getValue();
-
-          if (
-            namedTypes.ImportDefaultSpecifier.check(value) ||
-              namedTypes.ImportNamespaceSpecifier.check(value)
-          ) {
-            assert.strictEqual(foundImportSpecifier, false);
-          } else {
-            namedTypes.ImportSpecifier.assert(value);
+      path.each(function(specifierPath) {
+        var value = specifierPath.getValue();
+        if (
+          namedTypes.ImportDefaultSpecifier.check(value) ||
+          namedTypes.ImportNamespaceSpecifier.check(value)
+        ) {
+          standalones.push(print(specifierPath));
+        } else {
+          grouped.push(print(specifierPath));
+        }
+      }, "specifiers");
 
-            if (!foundImportSpecifier) {
-              foundImportSpecifier = true;
+      assert.ok(standalones.length <= 1);
 
-              parts.push(options.bracketSpacing ? "{ " : "{");
-            }
-          }
+      if (standalones.length > 0) {
+        parts.push(standalones[0]);
+      }
 
-          parts.push(print(specifierPath));
-        },
-        "specifiers"
-      );
+      if (standalones.length > 0 && grouped.length > 0) {
+        parts.push(", ");
+      }
 
-      if (foundImportSpecifier) {
-        parts.push(options.bracketSpacing ? " }" : "}");
+      if (grouped.length > 0) {
+        parts.push(
+          group(concat([
+            "{",
+            indent(
+              options.tabWidth,
+              concat([
+                options.bracketSpacing ? line : softline,
+                join(concat([",", options.bracketSpacing ? line : softline]),
+                     grouped)
+              ])
+            ),
+            ifBreak(options.trailingComma ? "," : ""),
+            options.bracketSpacing ? line : softline,
+            "}",
+          ]))
+        );
       }
 
       parts.push(" from ");
@@ -866,7 +874,15 @@ function genericPrintNoParens(path, options, print) {
 
     return group(concat(parts));
   case "ForStatement":
-    // TODO Get the for (;;) case right.
+    const body = adjustClause(path.call(print, "body"), options);
+
+    if (!n.init && !n.test && !n.update) {
+      return concat([
+        'for (;;)',
+        body,
+      ]);
+    }
+
     return concat([
       "for (",
       group(
@@ -888,7 +904,7 @@ function genericPrintNoParens(path, options, print) {
         ])
       ),
       ")",
-      adjustClause(path.call(print, "body"), options)
+      body,
     ]);
   case "WhileStatement":
     return concat([
@@ -1876,6 +1892,7 @@ function printExportDeclaration(path, options, print) {
                    path.map(print, "specifiers"))
             ])
           ),
+          ifBreak(options.trailingComma ? "," : ""),
           options.bracketSpacing ? line : softline,
           "}"
         ]))


Test patch:
diff --git a/tests/bracketSpacing/__snapshots__/jsfmt.spec.js.snap b/tests/bracketSpacing/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..69d639e88
--- /dev/null
+++ b/tests/bracketSpacing/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,35 @@
+exports[`test array.js 1`] = `
+"const arr1 = [1,2,3,4];
+const arr2 = [1, 2, 3, 4];
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const arr1 = [ 1, 2, 3, 4 ];
+const arr2 = [ 1, 2, 3, 4 ];
+"
+`;
+
+exports[`test array.js 2`] = `
+"const arr1 = [1,2,3,4];
+const arr2 = [1, 2, 3, 4];
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const arr1 = [1, 2, 3, 4];
+const arr2 = [1, 2, 3, 4];
+"
+`;
+
+exports[`test object.js 1`] = `
+"const obj1 = {a:1, b:2, c:3}
+const obj2 = { a:1, b:2, c:3 };
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const obj1 = { a: 1, b: 2, c: 3 };
+const obj2 = { a: 1, b: 2, c: 3 };
+"
+`;
+
+exports[`test object.js 2`] = `
+"const obj1 = {a:1, b:2, c:3}
+const obj2 = { a:1, b:2, c:3 };
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const obj1 = {a: 1, b: 2, c: 3};
+const obj2 = {a: 1, b: 2, c: 3};
+"
+`;
diff --git a/tests/bracketSpacing/array.js b/tests/bracketSpacing/array.js
new file mode 100644
index 000000000..967d82c21
--- /dev/null
+++ b/tests/bracketSpacing/array.js
@@ -0,0 +1,2 @@
+const arr1 = [1,2,3,4];
+const arr2 = [1, 2, 3, 4];
diff --git a/tests/bracketSpacing/jsfmt.spec.js b/tests/bracketSpacing/jsfmt.spec.js
new file mode 100644
index 000000000..7d17b3723
--- /dev/null
+++ b/tests/bracketSpacing/jsfmt.spec.js
@@ -0,0 +1,3 @@
+run_spec(__dirname);
+
+run_spec(__dirname, { bracketSpacing: false });
diff --git a/tests/bracketSpacing/object.js b/tests/bracketSpacing/object.js
new file mode 100644
index 000000000..693616c04
--- /dev/null
+++ b/tests/bracketSpacing/object.js
@@ -0,0 +1,2 @@
+const obj1 = {a:1, b:2, c:3}
+const obj2 = { a:1, b:2, c:3 };
diff --git a/tests/config_module_name_mapper_filetype/__snapshots__/jsfmt.spec.js.snap b/tests/config_module_name_mapper_filetype/__snapshots__/jsfmt.spec.js.snap
index b81d9d8a8..ab98a17c5 100644
--- a/tests/config_module_name_mapper_filetype/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/config_module_name_mapper_filetype/__snapshots__/jsfmt.spec.js.snap
@@ -7,6 +7,8 @@ import {doesntExist} from \"./SomeCSSFile.css\"; // Error: \`doestExist\` isn\'t
 // @flow
 
 import { className } from \"./SomeCSSFile.css\";
-import { doesntExist } from \"./SomeCSSFile.css\"; // Error: \`doestExist\` isn\'t an export
+import {
+  doesntExist
+} from \"./SomeCSSFile.css\"; // Error: \`doestExist\` isn\'t an export
 "
 `;
diff --git a/tests/declare_export/__snapshots__/jsfmt.spec.js.snap b/tests/declare_export/__snapshots__/jsfmt.spec.js.snap
index d9a5c75e7..fa2f9202e 100644
--- a/tests/declare_export/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/declare_export/__snapshots__/jsfmt.spec.js.snap
@@ -1144,7 +1144,10 @@ import { specifierNumber } from \"ES6_Named1\";
 ///////////////////////////////////////////////////
 // == Multi \`export *\` should combine exports == //
 ///////////////////////////////////////////////////
-import { numberValue1 as numberValue8, numberValue2 as numberValue9 } from \"./ES6_ExportAllFromMulti\";
+import {
+  numberValue1 as numberValue8,
+  numberValue2 as numberValue9
+} from \"./ES6_ExportAllFromMulti\";
 
 var at1: number = numberValue8;
 var at2: string = numberValue8;
diff --git a/tests/es6modules/__snapshots__/jsfmt.spec.js.snap b/tests/es6modules/__snapshots__/jsfmt.spec.js.snap
index f5c5abbef..b04c6bf61 100644
--- a/tests/es6modules/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/es6modules/__snapshots__/jsfmt.spec.js.snap
@@ -1275,7 +1275,10 @@ import { specifierNumber } from \"ES6_Named1\";
 ///////////////////////////////////////////////////
 // == Multi \`export *\` should combine exports == //
 ///////////////////////////////////////////////////
-import { numberValue1 as numberValue8, numberValue2 as numberValue9 } from \"./ES6_ExportAllFromMulti\";
+import {
+  numberValue1 as numberValue8,
+  numberValue2 as numberValue9
+} from \"./ES6_ExportAllFromMulti\";
 
 var at1: number = numberValue8;
 var at2: string = numberValue8;
@@ -1286,7 +1289,9 @@ var at4: string = numberValue9;
 /////////////////////////////////////////////////////////////
 // == Vanilla \`import\` cannot import a type-only export == //
 /////////////////////////////////////////////////////////////
-import { typeAlias } from \"./ExportType\"; // Error: Cannot vanilla-import a type alias!
+import {
+  typeAlias
+} from \"./ExportType\"; // Error: Cannot vanilla-import a type alias!
 "
 `;
 
diff --git a/tests/export_type/__snapshots__/jsfmt.spec.js.snap b/tests/export_type/__snapshots__/jsfmt.spec.js.snap
index e4cbb356c..cb79c85b4 100644
--- a/tests/export_type/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/export_type/__snapshots__/jsfmt.spec.js.snap
@@ -51,7 +51,12 @@ var n: IFoo2 = {prop: 42}; // Error: {prop:number} ~> {prop:string}
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 /* @flow */
 
-import type { inlinedType1, standaloneType1, talias1, talias3 } from \"./types_only\";
+import type {
+  inlinedType1,
+  standaloneType1,
+  talias1,
+  talias3
+} from \"./types_only\";
 
 var a: inlinedType1 = 42;
 var b: inlinedType1 = \"asdf\";
diff --git a/tests/for/__snapshots__/jsfmt.spec.js.snap b/tests/for/__snapshots__/jsfmt.spec.js.snap
index 13b0549ec..2f651c203 100644
--- a/tests/for/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/for/__snapshots__/jsfmt.spec.js.snap
@@ -280,3 +280,12 @@ function corge(x: boolean) {
 }
 "
 `;
+
+exports[`test for.js 1`] = `
+"for (;;) {}
+for (var i = 0; i < 10; ++i) {}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+for (;;) {}
+for (var i = 0; i < 10; ++i) {}
+"
+`;
diff --git a/tests/for/for.js b/tests/for/for.js
new file mode 100644
index 000000000..2f4f7ad1a
--- /dev/null
+++ b/tests/for/for.js
@@ -0,0 +1,2 @@
+for (;;) {}
+for (var i = 0; i < 10; ++i) {}
diff --git a/tests/quotes/__snapshots__/jsfmt.spec.js.snap b/tests/quotes/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..cbc3e7bd5
--- /dev/null
+++ b/tests/quotes/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,123 @@
+exports[`test functions.js 1`] = `
+"const a = () => \"Foo bar\";
+
+function b(object, key) {
+  return object[\'key\'];
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = () => \"Foo bar\";
+
+function b(object, key) {
+  return object[\"key\"];
+}
+"
+`;
+
+exports[`test functions.js 2`] = `
+"const a = () => \"Foo bar\";
+
+function b(object, key) {
+  return object[\'key\'];
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = () => \'Foo bar\';
+
+function b(object, key) {
+  return object[\'key\'];
+}
+"
+`;
+
+exports[`test strings.js 1`] = `
+"\"abc\"
+\'abc\'
+
+\'\\\'\'
+
+\'\"\'
+\'\\\"\'
+\'\\\\\"\'
+
+\"\'\"
+\"\\\'\"
+\"\\\\\'\"
+
+\"\'\\\"\"
+\'\\\'\"\'
+
+\'\\\\\'
+\"\\\\\"
+
+\'\\0\'
+\'🐶\'
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\"abc\";
+\"abc\";
+
+\"\'\";
+
+\'\"\';
+\'\"\';
+\'\\\\\"\';
+
+\"\'\";
+\"\'\";
+\"\\\\\'\";
+
+\"\'\\\"\";
+\"\'\\\"\";
+
+\"\\\\\";
+\"\\\\\";
+
+\"\\u0000\";
+\"🐶\";
+"
+`;
+
+exports[`test strings.js 2`] = `
+"\"abc\"
+\'abc\'
+
+\'\\\'\'
+
+\'\"\'
+\'\\\"\'
+\'\\\\\"\'
+
+\"\'\"
+\"\\\'\"
+\"\\\\\'\"
+
+\"\'\\\"\"
+\'\\\'\"\'
+
+\'\\\\\'
+\"\\\\\"
+
+\'\\0\'
+\'🐶\'
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+\'abc\';
+\'abc\';
+
+\"\'\";
+
+\'\"\';
+\'\"\';
+\'\\\\\"\';
+
+\"\'\";
+\"\'\";
+\"\\\\\'\";
+
+\'\\\'\"\';
+\'\\\'\"\';
+
+\'\\\\\';
+\'\\\\\';
+
+\'\\u0000\';
+\'🐶\';
+"
+`;
diff --git a/tests/quotes/functions.js b/tests/quotes/functions.js
new file mode 100644
index 000000000..56fdd5bce
--- /dev/null
+++ b/tests/quotes/functions.js
@@ -0,0 +1,5 @@
+const a = () => "Foo bar";
+
+function b(object, key) {
+  return object['key'];
+}
diff --git a/tests/quotes/jsfmt.spec.js b/tests/quotes/jsfmt.spec.js
new file mode 100644
index 000000000..821d6e5ee
--- /dev/null
+++ b/tests/quotes/jsfmt.spec.js
@@ -0,0 +1,3 @@
+run_spec(__dirname);
+
+run_spec(__dirname, { singleQuote: true });
diff --git a/tests/quotes/strings.js b/tests/quotes/strings.js
new file mode 100644
index 000000000..b296b144d
--- /dev/null
+++ b/tests/quotes/strings.js
@@ -0,0 +1,21 @@
+"abc"
+'abc'
+
+'\''
+
+'"'
+'\"'
+'\\"'
+
+"'"
+"\'"
+"\\'"
+
+"'\""
+'\'"'
+
+'\\'
+"\\"
+
+'\0'
+'🐶'
diff --git a/tests/tabWith/__snapshots__/jsfmt.spec.js.snap b/tests/tabWith/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..e583af41a
--- /dev/null
+++ b/tests/tabWith/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,105 @@
+exports[`test class.js 1`] = `
+"class A {
+  method() {
+    var x = 1;
+    while(typeof x == \"number\" || typeof x == \"string\") {
+        x = x + 1;
+        if (true) x = \"\";
+    }
+    var z:number = x;
+  }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class A {
+  method() {
+    var x = 1;
+    while (typeof x == \"number\" || typeof x == \"string\") {
+      x = x + 1;
+      if (true)
+        x = \"\";
+    }
+    var z: number = x;
+  }
+}
+"
+`;
+
+exports[`test class.js 2`] = `
+"class A {
+  method() {
+    var x = 1;
+    while(typeof x == \"number\" || typeof x == \"string\") {
+        x = x + 1;
+        if (true) x = \"\";
+    }
+    var z:number = x;
+  }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class A {
+    method() {
+        var x = 1;
+        while (typeof x == \"number\" || typeof x == \"string\") {
+            x = x + 1;
+            if (true)
+                x = \"\";
+        }
+        var z: number = x;
+    }
+}
+"
+`;
+
+exports[`test nested-functions.spec.js 1`] = `
+"const c = () => {};
+
+function a() {
+  return function b() {
+    return () => {
+      return function() {
+        return c;
+      }
+    }
+  }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const c = () => {};
+
+function a() {
+  return function b() {
+    return () => {
+      return function() {
+        return c;
+      };
+    };
+  };
+}
+"
+`;
+
+exports[`test nested-functions.spec.js 2`] = `
+"const c = () => {};
+
+function a() {
+  return function b() {
+    return () => {
+      return function() {
+        return c;
+      }
+    }
+  }
+}
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const c = () => {};
+
+function a() {
+    return function b() {
+        return () => {
+            return function() {
+                return c;
+            };
+        };
+    };
+}
+"
+`;
diff --git a/tests/tabWith/class.js b/tests/tabWith/class.js
new file mode 100644
index 000000000..3e07e843a
--- /dev/null
+++ b/tests/tabWith/class.js
@@ -0,0 +1,10 @@
+class A {
+  method() {
+    var x = 1;
+    while(typeof x == "number" || typeof x == "string") {
+        x = x + 1;
+        if (true) x = "";
+    }
+    var z:number = x;
+  }
+}
diff --git a/tests/tabWith/jsfmt.spec.js b/tests/tabWith/jsfmt.spec.js
new file mode 100644
index 000000000..862b4296f
--- /dev/null
+++ b/tests/tabWith/jsfmt.spec.js
@@ -0,0 +1,3 @@
+run_spec(__dirname);
+
+run_spec(__dirname, { tabWidth: 4 });
diff --git a/tests/tabWith/nested-functions.spec.js b/tests/tabWith/nested-functions.spec.js
new file mode 100644
index 000000000..8e121b753
--- /dev/null
+++ b/tests/tabWith/nested-functions.spec.js
@@ -0,0 +1,11 @@
+const c = () => {};
+
+function a() {
+  return function b() {
+    return () => {
+      return function() {
+        return c;
+      }
+    }
+  }
+}
diff --git a/tests/trailingComma/__snapshots__/jsfmt.spec.js.snap b/tests/trailingComma/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..d6a325b0a
--- /dev/null
+++ b/tests/trailingComma/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,123 @@
+exports[`test function-calls.js 1`] = `
+"const a = (param1, param2, param3) => {}
+
+a(\'value\', \'value2\', \'value3\');
+
+a(
+  \'a-long-value\',
+  \'a-really-really-long-value\',
+  \'a-really-really-really-long-value\',
+);
+
+a(\'value\', \'value2\', a(\'long-nested-value\', \'long-nested-value2\', \'long-nested-value3\'));
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = (param1, param2, param3) => {};
+
+a(\"value\", \"value2\", \"value3\");
+
+a(
+  \"a-long-value\",
+  \"a-really-really-long-value\",
+  \"a-really-really-really-long-value\"
+);
+
+a(
+  \"value\",
+  \"value2\",
+  a(\"long-nested-value\", \"long-nested-value2\", \"long-nested-value3\")
+);
+"
+`;
+
+exports[`test function-calls.js 2`] = `
+"const a = (param1, param2, param3) => {}
+
+a(\'value\', \'value2\', \'value3\');
+
+a(
+  \'a-long-value\',
+  \'a-really-really-long-value\',
+  \'a-really-really-really-long-value\',
+);
+
+a(\'value\', \'value2\', a(\'long-nested-value\', \'long-nested-value2\', \'long-nested-value3\'));
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = (param1, param2, param3) => {};
+
+a(\"value\", \"value2\", \"value3\");
+
+a(
+  \"a-long-value\",
+  \"a-really-really-long-value\",
+  \"a-really-really-really-long-value\",
+);
+
+a(
+  \"value\",
+  \"value2\",
+  a(\"long-nested-value\", \"long-nested-value2\", \"long-nested-value3\"),
+);
+"
+`;
+
+exports[`test object.js 1`] = `
+"const a = {
+  b: true,
+  c: {
+    c1: \'hello\'
+  },
+  d: false
+};
+
+const aLong = {
+  bHasALongName: \'a-long-value\',
+  cHasALongName: {
+    c1: \'a-really-long-value\',
+    c2: \'a-really-really-long-value\',
+  },
+  dHasALongName: \'a-long-value-too\'
+};
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = { b: true, c: { c1: \"hello\" }, d: false };
+
+const aLong = {
+  bHasALongName: \"a-long-value\",
+  cHasALongName: {
+    c1: \"a-really-long-value\",
+    c2: \"a-really-really-long-value\"
+  },
+  dHasALongName: \"a-long-value-too\"
+};
+"
+`;
+
+exports[`test object.js 2`] = `
+"const a = {
+  b: true,
+  c: {
+    c1: \'hello\'
+  },
+  d: false
+};
+
+const aLong = {
+  bHasALongName: \'a-long-value\',
+  cHasALongName: {
+    c1: \'a-really-long-value\',
+    c2: \'a-really-really-long-value\',
+  },
+  dHasALongName: \'a-long-value-too\'
+};
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const a = { b: true, c: { c1: \"hello\" }, d: false };
+
+const aLong = {
+  bHasALongName: \"a-long-value\",
+  cHasALongName: {
+    c1: \"a-really-long-value\",
+    c2: \"a-really-really-long-value\",
+  },
+  dHasALongName: \"a-long-value-too\",
+};
+"
+`;
diff --git a/tests/trailingComma/function-calls.js b/tests/trailingComma/function-calls.js
new file mode 100644
index 000000000..86e205476
--- /dev/null
+++ b/tests/trailingComma/function-calls.js
@@ -0,0 +1,11 @@
+const a = (param1, param2, param3) => {}
+
+a('value', 'value2', 'value3');
+
+a(
+  'a-long-value',
+  'a-really-really-long-value',
+  'a-really-really-really-long-value',
+);
+
+a('value', 'value2', a('long-nested-value', 'long-nested-value2', 'long-nested-value3'));
diff --git a/tests/trailingComma/jsfmt.spec.js b/tests/trailingComma/jsfmt.spec.js
new file mode 100644
index 000000000..61e3318df
--- /dev/null
+++ b/tests/trailingComma/jsfmt.spec.js
@@ -0,0 +1,3 @@
+run_spec(__dirname);
+
+run_spec(__dirname, { trailingComma: true });
diff --git a/tests/trailingComma/object.js b/tests/trailingComma/object.js
new file mode 100644
index 000000000..9bdc3b1f2
--- /dev/null
+++ b/tests/trailingComma/object.js
@@ -0,0 +1,16 @@
+const a = {
+  b: true,
+  c: {
+    c1: 'hello'
+  },
+  d: false
+};
+
+const aLong = {
+  bHasALongName: 'a-long-value',
+  cHasALongName: {
+    c1: 'a-really-long-value',
+    c2: 'a-really-really-long-value',
+  },
+  dHasALongName: 'a-long-value-too'
+};
diff --git a/tests_config/run_spec.js b/tests_config/run_spec.js
index 851303276..9be8940fe 100644
--- a/tests_config/run_spec.js
+++ b/tests_config/run_spec.js
@@ -17,14 +17,14 @@ function removeEmptyStatements(ast) {
   });
 }
 
-function run_spec(dirname) {
+function run_spec(dirname, options) {
   fs.readdirSync(dirname).forEach(filename => {
     if (filename.endsWith('.js') && filename !== 'jsfmt.spec.js') {
       const path = dirname + '/' + filename;
 
       if (!RUN_AST_TESTS) {
         const source = read(path).replace(/\r\n/g, '\n');
-        const output = prettyprint(source, path);
+        const output = prettyprint(source, path, options || {});
         test(filename, () => {
           expect(source + '~'.repeat(80) + '\n' + output).toMatchSnapshot();
         });
@@ -79,8 +79,12 @@ function parse(string) {
   return stripLocation(flowParser.parse(string));
 }
 
-function prettyprint(src, filename) {
-  return prettier.format(src, { filename, useFlowParser: true, printWidth: 80 });
+function prettyprint(src, filename, options) {
+  return prettier.format(src, Object.assign({
+    filename,
+    useFlowParser: true,
+    printWidth: 80,
+  }, options));
 }
 
 function read(filename) {