Repository: prettier/prettier (JavaScript)

Issue:
Prettier does not recognize test-statement when using "waitForAsync" instead of "async"
**Prettier 2.5.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuE8DOMAUByAZhCAAgCMBDAJ2wBpCB3UgSxgDEJyBBNATyjE0wCUhALwA+QsAA6UQoUhQMhfEWGFJIMuXUBuabLgAPAA5wwWZQIB0MCAFEAjgFdSAG0zrN6gdIC+AgdogVCAQRjAM0GjIoBTkELQAChQIUSiu9FxRwcTkpGAA1nAwAMqkALZwADIMUHDIuK5ocNm5BUXFRnk1AObIMOSOzSBwZcRwACbjE5WkUN3O3XCs5GWkMOFzyCCkjjZBIAAWMGUuAOoHTHBonWBwxSlMDABuTFxbYGhZIDVN5DAJuW6q3qjSGACs0AZij0XHAAIqOCDwEEuJrBTrkX5bMhjFz7IzkGowU4McYwA7IAAcAAZ0XEmqdckYtgSrnByE86sEnEi4ADQqltmgALS1CYTfbkOBOBhSgGkIGkFFokBNMoMPoDIZoGHwxHIpANVFDGCkYgkskUpAAJmC-UYLh6AGEIGVgSgrgBWfaOJoAFTNqSNKqegwAklAprBimBCWF2JHijAuLDlXAfD4gA)
<!-- prettier-ignore -->
```sh
--parser babel
```

**Input:**
<!-- prettier-ignore -->
```jsx
test('foo bar', waitForAsync(() => {
  const foo = "bar";
  expect(foo).toEqual("bar")
}));
```

**Output:**
<!-- prettier-ignore -->
```jsx
test(
  "foo bar",
  waitForAsync(() => {
    const foo = "bar";
    expect(foo).toEqual("bar");
  })
);

```

**Expected behavior:**
<!-- prettier-ignore -->
```jsx
test("foo bar", waitForAsync(() => {
  const foo = "bar";
  expect(foo).toEqual("bar");
}));


```

In this case, Prettier does not recognize this test-statement and so, incorrectly adds newlines before each argument. The issue here seems to be the `waitForAsync`. When using `async` it works as expected. This is not a realistic fix however, since async is being deprecated by Angular.


Hints:
Probably requires a simple addition to the method `isAngularTestWrapper` (in file `/src/language-js/utils.js`):
```
function isAngularTestWrapper(node) {
  return (
    isCallExpression(node) &&
    node.callee.type === "Identifier" &&
    (node.callee.name === "async" ||
      node.callee.name === "inject" ||
      node.callee.name === "fakeAsync")
  );
}
```
to
```
function isAngularTestWrapper(node) {
  return (
    isCallExpression(node) &&
    node.callee.type === "Identifier" &&
    (node.callee.name === "async" ||
      node.callee.name === "inject" ||
      node.callee.name === "fakeAsync" ||
      node.callee.name === "waitForAsync")
  );
}
```



Patch:
diff --git a/src/language-js/utils.js b/src/language-js/utils.js
--- a/src/language-js/utils.js
+++ b/src/language-js/utils.js
@@ -308,7 +308,10 @@ function isTemplateLiteral(node) {
 }
 
 /**
- * Note: `inject` is used in AngularJS 1.x, `async` in Angular 2+
+ * Note: `inject` is used in AngularJS 1.x, `async` and `fakeAsync` in
+ * Angular 2+, although `async` is deprecated and replaced by `waitForAsync`
+ * since Angular 12.
+ *
  * example: https://docs.angularjs.org/guide/unit-testing#using-beforeall-
  *
  * @param {CallExpression} node
@@ -318,9 +321,7 @@ function isAngularTestWrapper(node) {
   return (
     isCallExpression(node) &&
     node.callee.type === "Identifier" &&
-    (node.callee.name === "async" ||
-      node.callee.name === "inject" ||
-      node.callee.name === "fakeAsync")
+    ["async", "inject", "fakeAsync", "waitForAsync"].includes(node.callee.name)
   );
 }
 


Test patch:
diff --git a/tests/format/js/test-declarations/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/test-declarations/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/js/test-declarations/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/js/test-declarations/__snapshots__/jsfmt.spec.js.snap
@@ -276,6 +276,133 @@ function x() {
 ================================================================================
 `;
 
+exports[`angular_waitForAsync.js - {"arrowParens":"avoid"} format 1`] = `
+====================================options=====================================
+arrowParens: "avoid"
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+beforeEach(waitForAsync(() => {
+  // code
+}));
+
+afterAll(waitForAsync(() => {
+  console.log('Hello');
+}));
+
+it('should create the app', waitForAsync(() => {
+  //code
+}));
+
+it("does something really long and complicated so I have to write a very long name for the test", waitForAsync(() => {
+  // code
+}));
+
+it("does something really long and complicated so I have to write a very long name for the test", waitForAsync(() => new SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS));
+
+/*
+* isTestCall(parent) should only be called when parent exists
+* and parent.type is CallExpression. This test makes sure that
+* no errors are thrown when calling isTestCall(parent)
+*/
+function x() { waitForAsync(() => {}) }
+
+=====================================output=====================================
+beforeEach(waitForAsync(() => {
+  // code
+}));
+
+afterAll(waitForAsync(() => {
+  console.log("Hello");
+}));
+
+it("should create the app", waitForAsync(() => {
+  //code
+}));
+
+it("does something really long and complicated so I have to write a very long name for the test", waitForAsync(() => {
+  // code
+}));
+
+it("does something really long and complicated so I have to write a very long name for the test", waitForAsync(() =>
+  new SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS()));
+
+/*
+ * isTestCall(parent) should only be called when parent exists
+ * and parent.type is CallExpression. This test makes sure that
+ * no errors are thrown when calling isTestCall(parent)
+ */
+function x() {
+  waitForAsync(() => {});
+}
+
+================================================================================
+`;
+
+exports[`angular_waitForAsync.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "flow", "typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+beforeEach(waitForAsync(() => {
+  // code
+}));
+
+afterAll(waitForAsync(() => {
+  console.log('Hello');
+}));
+
+it('should create the app', waitForAsync(() => {
+  //code
+}));
+
+it("does something really long and complicated so I have to write a very long name for the test", waitForAsync(() => {
+  // code
+}));
+
+it("does something really long and complicated so I have to write a very long name for the test", waitForAsync(() => new SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS));
+
+/*
+* isTestCall(parent) should only be called when parent exists
+* and parent.type is CallExpression. This test makes sure that
+* no errors are thrown when calling isTestCall(parent)
+*/
+function x() { waitForAsync(() => {}) }
+
+=====================================output=====================================
+beforeEach(waitForAsync(() => {
+  // code
+}));
+
+afterAll(waitForAsync(() => {
+  console.log("Hello");
+}));
+
+it("should create the app", waitForAsync(() => {
+  //code
+}));
+
+it("does something really long and complicated so I have to write a very long name for the test", waitForAsync(() => {
+  // code
+}));
+
+it("does something really long and complicated so I have to write a very long name for the test", waitForAsync(() =>
+  new SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS()));
+
+/*
+ * isTestCall(parent) should only be called when parent exists
+ * and parent.type is CallExpression. This test makes sure that
+ * no errors are thrown when calling isTestCall(parent)
+ */
+function x() {
+  waitForAsync(() => {});
+}
+
+================================================================================
+`;
+
 exports[`angularjs_inject.js - {"arrowParens":"avoid"} format 1`] = `
 ====================================options=====================================
 arrowParens: "avoid"
diff --git a/tests/format/js/test-declarations/angular_waitForAsync.js b/tests/format/js/test-declarations/angular_waitForAsync.js
new file mode 100644
--- /dev/null
+++ b/tests/format/js/test-declarations/angular_waitForAsync.js
@@ -0,0 +1,24 @@
+beforeEach(waitForAsync(() => {
+  // code
+}));
+
+afterAll(waitForAsync(() => {
+  console.log('Hello');
+}));
+
+it('should create the app', waitForAsync(() => {
+  //code
+}));
+
+it("does something really long and complicated so I have to write a very long name for the test", waitForAsync(() => {
+  // code
+}));
+
+it("does something really long and complicated so I have to write a very long name for the test", waitForAsync(() => new SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS));
+
+/*
+* isTestCall(parent) should only be called when parent exists
+* and parent.type is CallExpression. This test makes sure that
+* no errors are thrown when calling isTestCall(parent)
+*/
+function x() { waitForAsync(() => {}) }