Repository: abuiles/ember-watson
Pull request #112 (2017-06-23 23:37:04)

Add mocha tests conversion to async/await
Converts from the `andThen` style acceptance tests...
```js
it('can visit subroutes', function(done) {
  visit('/');

  andThen(function() {
    expect(find('h2').text()).to.be.empty;
  });

  visit('/foo');

  andThen(function() {
    expect(find('h2').text()).to.be.equal('this is an acceptance test');
    done();
  });
});
```
... into cleaner async/await style:
```js
it('can visit subroutes', async function() {
  await visit('/');

  expect(find('h2').text()).to.be.empty;

  await visit('/foo');

  expect(find('h2').text()).to.be.equal('this is an acceptance test');
});
```

I only implemented mocha since that's what I use, but qunit could be added ([ember-power-select](https://github.com/cibernox/ember-power-select/blob/master/tests/acceptance/helpers-test.js) has some example qunit tests using async/await style).

It doesn't handle custom async test helpers which need to be awaited. Maybe there could be a config option to list the names of additional async helpers.

It doesn't handle naming/scoping conflicts when merging `andThen` blocks. Babel will catch them if the code is invalid (e.g. reassign to `const`), otherwise the linter will catch them. For example:
```js
andThen(function() {
  let foo = 1;
});
andThen(function() {
  let foo = 2;
});

// will become:
let foo = 1;
let foo = 2;
```
\

knownasilya: I updated Travis and rebased this branch. Just waiting on Travis to finish its thing.

Would be nice to have this for QUnit, since that's probably more widely used since it's the default. Otherwise, this looks A+
knownasilya: @dwickern could you update the README.md with your command. Just follow how the other commands are documented. Once that's done, I'll be happy to merge. Thanks again!
dwickern: I updated the readme. It was easy to add qunit support too, although I haven't run it against an actual qunit project
knownasilya: Beautiful!

Patch:
diff --git a/README.md b/README.md
index dda9836..8009140 100644
--- a/README.md
+++ b/README.md
@@ -119,6 +119,43 @@ helper [introduced](https://github.com/ember-cli/ember-cli/pull/4772) in
 Ember CLI 1.13.9.
 
 
+### Tests: Convert `andThen` style async tests to `async/await`
+
+```sh
+ember watson:convert-tests-to-async-await <path>
+```
+
+Convert (qunit or mocha flavored) acceptance tests to utilize `async/await`
+
+```js
+// before:
+it('can visit subroutes', function(done) {
+  visit('/');
+
+  andThen(function() {
+    expect(find('h2').text()).to.be.empty;
+  });
+
+  visit('/foo');
+
+  andThen(function() {
+    expect(find('h2').text()).to.be.equal('this is an acceptance test');
+    done();
+  });
+});
+
+// after:
+it('can visit subroutes', async function() {
+  await visit('/');
+
+  expect(find('h2').text()).to.be.empty;
+
+  await visit('/foo');
+
+  expect(find('h2').text()).to.be.equal('this is an acceptance test');
+});
+```
+
 ### Ember Data: Async Relationships Default
 
 ```sh
diff --git a/lib/cli.js b/lib/cli.js
index bc5e31d..64ab6aa 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -107,8 +107,17 @@ program
   .description('Remove `isNewSerializerAPI` in Ember Data serializers.')
   .option('--dry-run', 'Don\'t modify files, output JSON instead')
   .action(function(path, options) {
-    path = path || 'app/serializers';
-    watson.removeEmberDataIsNewSerializerAPI(path, options.dryRun);
+      path = path || 'app/serializers';
+      watson.removeEmberDataIsNewSerializerAPI(path, options.dryRun);
+  });
+
+program
+  .command('convert-tests-to-async-await [testsPath]')
+  .description('Convert tests to use async/await')
+  .option('--dry-run', 'Don\'t modify files, output JSON instead')
+  .action(function(testsPath, options) {
+      testsPath = testsPath || 'tests';
+      watson.transformTestsToAsyncAwait(testsPath, options.dryRun);
   });
 
 program
diff --git a/lib/commands/index.js b/lib/commands/index.js
index f378f57..4cd08db 100644
--- a/lib/commands/index.js
+++ b/lib/commands/index.js
@@ -12,5 +12,6 @@ module.exports = {
   'watson:use-destroy-app-helper': require('./use-destroy-app-helper'),
   'watson:remove-ember-k': require('./remove-ember-k'),
   'watson:replace-needs-with-injection': require('./replace-needs-with-injection'),
-  'watson:remove-ember-data-is-new-serializer-api': require('./remove-ember-data-is-new-serializer-api')
+  'watson:remove-ember-data-is-new-serializer-api': require('./remove-ember-data-is-new-serializer-api'),
+  'watson:convert-mocha-tests-to-async-await': require('./convert-tests-to-async-await')
 };
diff --git a/lib/watson.js b/lib/watson.js
index f3fbe4e..c1ac648 100644
--- a/lib/watson.js
+++ b/lib/watson.js
@@ -16,6 +16,7 @@ var transformDestroyApp = require('./formulas/destroy-app-transform');
 var removeEmberK = require('./formulas/remove-ember-k');
 var replaceNeedsWithInjection = require('./formulas/replace-needs-with-injection');
 var removeEmberDataIsNewSerializerAPI = require('./formulas/remove-ember-data-is-new-serializer-api');
+var transformTestsToAsyncAwait = require('./formulas/convert-tests-to-async-await');
 
 module.exports = EmberWatson;
 
@@ -112,8 +113,18 @@ EmberWatson.prototype.findOverloadedCPs = function(rootPath, dryRun) {
     return source;
   }, dryRun);
   return searcher;
-}
-;
+};
+
+EmberWatson.prototype._transformTestsToAsyncAwait = transformTestsToAsyncAwait;
+
+EmberWatson.prototype.transformTestsToAsyncAwait = function(rootPath, dryRun) {
+    var tests  = findFiles(rootPath, '.js').filter(function(file){
+        return file.indexOf('-test.js') > 0;
+    });
+
+    transform(tests, this._transformTestsToAsyncAwait, dryRun);
+};
+
 function transform(files, transformFormula, dryRun) {
   var wontFix = [];
 


Test patch:
diff --git a/lib/commands/convert-mocha-tests-to-async-await.js b/lib/commands/convert-mocha-tests-to-async-await.js
new file mode 100644
index 0000000..399f7b1
--- /dev/null
+++ b/lib/commands/convert-mocha-tests-to-async-await.js
@@ -0,0 +1,20 @@
+'use strict';
+
+var Watson = require('../../index');
+var watson = new Watson();
+
+module.exports = {
+  name: 'watson:convert-tests-to-async-await',
+  description: 'Convert tests to use async/await',
+  works: 'insideProject',
+  anonymousOptions: [
+    '<path>'
+  ],
+  availableOptions: [
+    { name: 'dry-run', type: Boolean, description: 'Run the command in dry-run mode (outputs JSON, non-destructive)', default: false }
+  ],
+  run: function(commandOptions, rawArgs) {
+    var path = rawArgs[0] ||  'tests';
+    watson.transformTestsToAsyncAwait(path, commandOptions.dryRun);
+  }
+};
diff --git a/lib/formulas/convert-tests-to-async-await.js b/lib/formulas/convert-tests-to-async-await.js
new file mode 100644
index 0000000..a6152c9
--- /dev/null
+++ b/lib/formulas/convert-tests-to-async-await.js
@@ -0,0 +1,282 @@
+/*jshint esversion: 6 */
+const recast      = require('recast');
+const builders    = recast.types.builders;
+const types       = recast.types.namedTypes;
+const parseAst    = require('../helpers/parse-ast');
+
+// TODO await custom async helpers
+const ASYNC_HELPERS = [
+  'click',
+  'fillIn',
+  'visit',
+  'keyEvent',
+  'triggerEvent',
+  'wait'
+];
+
+/**
+ * it('test name', function() { ... })
+ */
+function isMochaTest(node) {
+  return types.CallExpression.check(node.expression) &&
+    node.expression.callee.name === 'it' &&
+    node.expression.arguments.length === 2;
+}
+
+/**
+ * test('test name', function(assert) { ... })
+ */
+function isQunitTest(node) {
+  return types.CallExpression.check(node.expression) &&
+    node.expression.callee.name === 'test' &&
+    node.expression.arguments.length === 2;
+}
+
+/**
+ * it.skip('test name', function() { ... })
+ */
+function isMochaSkip(node) {
+  return types.CallExpression.check(node.expression) &&
+    types.MemberExpression.check(node.expression.callee) &&
+    node.expression.callee.object.name === 'it' &&
+    node.expression.callee.property.name === 'skip' &&
+    node.expression.arguments.length === 2;
+}
+
+function isTest(node) {
+  return isMochaTest(node) || isMochaSkip(node) || isQunitTest(node);
+}
+
+/**
+ * Only transform old-style async tests which use the `andThen` helper:
+ *
+ *   it('does something async', function() {
+ *     visit('/route')
+ *     andThen(function() {
+ *       assert(...)
+ *     })
+ *   })
+ */
+function testUsesAndThen(node) {
+  let callback = node.expression.arguments[1];
+  return (types.FunctionExpression.check(callback) || types.ArrowFunctionExpression.check(callback)) &&
+    types.BlockStatement.check(callback.body) &&
+    callback.body.body.find(isAndThen);
+}
+
+/**
+ * Whether the test uses mocha's `done` callback:
+ *
+ *   it('does something async', function(done) {
+ *     visit('/route')
+ *     andThen(function() {
+ *       assert(...)
+ *       done()
+ *     })
+ *   })
+ */
+function testUsesDone(node) {
+  let callback = node.expression.arguments[1];
+  return (types.FunctionExpression.check(callback) || types.ArrowFunctionExpression.check(callback)) &&
+    callback.params.length > 0 &&
+    callback.params[0].name === 'done';
+}
+
+/**
+ * visit(...), fillIn(...), etc.
+ */
+function isAsyncHelper(node) {
+  return types.CallExpression.check(node.expression) &&
+    ASYNC_HELPERS.indexOf(node.expression.callee.name) !== -1;
+}
+
+/**
+ * andThen(function() { ... })
+ */
+function isAndThen(node) {
+  return types.CallExpression.check(node.expression) &&
+    node.expression.callee.name === 'andThen' &&
+    node.expression.arguments.length === 1 &&
+    (types.FunctionExpression.check(node.expression.arguments[0]) ||
+    types.ArrowFunctionExpression.check(node.expression.arguments[0]));
+}
+
+/**
+ * done()
+ */
+function isDone(node) {
+  // TODO: handle callback named something other than "done"
+  return types.CallExpression.check(node.expression) &&
+    node.expression.callee.name === 'done' &&
+    node.expression.arguments.length === 0;
+}
+
+/**
+ * For example, this usage of `done` is not safe to remove:
+ *
+ *   it('fetches contacts', function(done) {
+ *     visit('/');
+ *     server.get('/contacts', (db, request) => {
+ *       done();
+ *     });
+ *   });
+ */
+function isDoneSafeToRemove(path) {
+  for (let parent = path.parent; !isTest(parent.node); parent = parent.parent) {
+    let node = parent.node;
+    if (types.CallExpression.check(node.expression) && !isAndThen(node)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+/**
+ * Replace `done` callback with async function
+ *
+ * Before:
+ *   it('tests something', function(done) { ... })
+ *
+ * After:
+ *   it('tests something', async function() { ... })
+ */
+function transformTestStatement(path, removeDone) {
+  let callback = path.node.expression.arguments[1];
+  callback.async = true;
+
+  if (removeDone) {
+    if (callback.params.length > 0 && types.Identifier.check(callback.params[0]) && callback.params[0].name === 'done') {
+      callback.params.shift();
+    }
+  }
+}
+
+/**
+ * Await async helpers
+ *
+ * Before:
+ *   visit('/route')
+ *
+ * After:
+ *   await visit('/route')
+ */
+function transformHelpers(path) {
+  path.node.expression = builders.awaitExpression(path.node.expression);
+}
+
+/**
+ * Remove andThen(...)
+ *
+ * Before:
+ *   foo();
+ *   andThen(function() {
+ *     assert(...)
+ *   })
+ *   bar();
+ *
+ * After:
+ *   foo();
+ *   assert(...)
+ *   bar();
+ */
+function transformAndThen(path) {
+  // TODO: handle naming conflicts when merging scopes
+  let outerStatements = path.parent.node.body;
+  let idx = outerStatements.indexOf(path.node);
+  if (idx !== -1) {
+    let innerStatements = path.node.expression.arguments[0].body.body;
+    outerStatements.splice(idx, 1, ...innerStatements);
+  }
+}
+
+/**
+ * Remove calls to done()
+ *
+ * Before:
+ *   andThen(function() {
+ *     assert(...)
+ *     done();
+ *   })
+ *
+ * After:
+ *   andThen(function() {
+ *     assert(...)
+ *   })
+ */
+function transformDone(path) {
+  let statements = path.parent.node.body;
+  let idx = statements.indexOf(path.node);
+  if (idx !== -1) {
+      statements.splice(idx, 1);
+  }
+}
+
+module.exports = function transform(source) {
+  const ast = parseAst(source);
+
+  const tests = [];
+  let currentTest;
+
+  recast.visit(ast, {
+    visitExpressionStatement(path) {
+      let node = path.node;
+
+      if (isTest(node)) {
+        if (!testUsesAndThen(node)) {
+          // don't transform this test
+          return false;
+        }
+
+        currentTest = {
+          test: path,
+          asyncHelpers: [],
+          andThens: [],
+          dones: [],
+          removeDone: testUsesDone(path.node)
+        };
+        tests.push(currentTest);
+      }
+
+      if (currentTest) {
+        if (isAsyncHelper(node)) {
+          currentTest.asyncHelpers.push(path);
+        }
+
+        if (isAndThen(node)) {
+          currentTest.andThens.push(path);
+        }
+
+        if (isDone(node)) {
+          if (isDoneSafeToRemove(path)) {
+            currentTest.dones.push(path);
+          } else {
+            currentTest.removeDone = false;
+          }
+        }
+      }
+
+      this.traverse(path);
+    },
+  });
+
+  tests.forEach(function({ test, asyncHelpers, andThens, dones, removeDone }) {
+    transformTestStatement(test, removeDone);
+
+    asyncHelpers.forEach(function(path) {
+      transformHelpers(path);
+    });
+
+    // process before `andThen` transform so the parent node still exists
+    dones.forEach(function(path) {
+      transformDone(path);
+    });
+
+    // process in reverse to handle nested `andThen`
+    andThens.reverse().forEach(function(path) {
+      transformAndThen(path);
+    });
+  });
+
+  return recast.print(ast, { tabWidth: 2, quote: 'single' }).code;
+};
diff --git a/tests/convert-tests-to-async-await-test.js b/tests/convert-tests-to-async-await-test.js
new file mode 100644
index 0000000..2cf731f
--- /dev/null
+++ b/tests/convert-tests-to-async-await-test.js
@@ -0,0 +1,62 @@
+'use strict';
+
+var Watson      = require('../index.js');
+var fs          = require('fs');
+var astEquality = require('./helpers/ast-equality');
+
+describe('Mocha async transformation', function() {
+  var baseDir = './tests/fixtures/convert-tests-to-async-await';
+  var watson;
+
+  beforeEach(function() {
+    watson = new Watson();
+  });
+
+  it('transforms async acceptance test using `done()` callback', function() {
+    var source            = fs.readFileSync(baseDir + '/old-acceptance-test-with-done.js');
+    var newSource         = watson._transformTestsToAsyncAwait(source);
+    var expectedNewSource = fs.readFileSync(baseDir + '/new-acceptance-test-with-done.js');
+
+    astEquality(newSource, expectedNewSource);
+  });
+
+  it('transforms async acceptance test without `done()`', function() {
+    var source            = fs.readFileSync(baseDir + '/old-acceptance-test-without-done.js');
+    var newSource         = watson._transformTestsToAsyncAwait(source);
+    var expectedNewSource = fs.readFileSync(baseDir + '/new-acceptance-test-without-done.js');
+
+    astEquality(newSource, expectedNewSource);
+  });
+
+  it('transforms async acceptance test nested `andThen`s', function() {
+    var source            = fs.readFileSync(baseDir + '/old-acceptance-test-with-nested-andthen.js');
+    var newSource         = watson._transformTestsToAsyncAwait(source);
+    var expectedNewSource = fs.readFileSync(baseDir + '/new-acceptance-test-with-nested-andthen.js');
+
+    astEquality(newSource, expectedNewSource);
+  });
+
+  it('leaves synchronous tests as-is', function() {
+    var source            = fs.readFileSync(baseDir + '/acceptance-test-synchronous.js');
+    var newSource         = watson._transformTestsToAsyncAwait(source);
+    var expectedNewSource = fs.readFileSync(baseDir + '/acceptance-test-synchronous.js');
+
+    astEquality(newSource, expectedNewSource);
+  });
+
+  it('leaves `done` callback as-is when used outside of `andThen`', function() {
+    var source            = fs.readFileSync(baseDir + '/old-acceptance-test-with-callback.js');
+    var newSource         = watson._transformTestsToAsyncAwait(source);
+    var expectedNewSource = fs.readFileSync(baseDir + '/new-acceptance-test-with-callback.js');
+
+    astEquality(newSource, expectedNewSource);
+  });
+
+  it('transforms async qunit test', function() {
+    var source            = fs.readFileSync(baseDir + '/old-qunit.js');
+    var newSource         = watson._transformTestsToAsyncAwait(source);
+    var expectedNewSource = fs.readFileSync(baseDir + '/new-qunit.js');
+
+    astEquality(newSource, expectedNewSource);
+  });
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/acceptance-test-synchronous.js b/tests/fixtures/convert-tests-to-async-await/acceptance-test-synchronous.js
new file mode 100644
index 0000000..e440625
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/acceptance-test-synchronous.js
@@ -0,0 +1,10 @@
+import Ember from 'ember';
+import { describe, it, beforeEach, afterEach } from 'mocha';
+
+const { expect } = window.chai;
+
+describe('basic acceptance test', function() {
+  it('can visit subroutes', function() {
+    expect(find('h2').text()).to.be.empty;
+  });
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-with-callback.js b/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-with-callback.js
new file mode 100644
index 0000000..bdd87c2
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-with-callback.js
@@ -0,0 +1,22 @@
+import Ember from 'ember';
+import { describe, it, beforeEach, afterEach } from 'mocha';
+
+const { expect } = window.chai;
+
+describe('basic acceptance test', function() {
+  it('can visit subroutes', async function(done) {
+    await visit('/');
+
+    expect(find('h2').text()).to.be.empty;
+
+    await visit('/foo');
+
+    expect(find('h2').text()).to.be.equal('this is an acceptance test');
+
+    server.put('/contacts', (db, request) => {
+      let params = JSON.parse(request.requestBody).contact;
+      assert.deepEqual(params, {});
+      done();
+    });
+  });
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-with-done.js b/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-with-done.js
new file mode 100644
index 0000000..3852f50
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-with-done.js
@@ -0,0 +1,16 @@
+import Ember from 'ember';
+import { describe, it, beforeEach, afterEach } from 'mocha';
+
+const { expect } = window.chai;
+
+describe('basic acceptance test', function() {
+  it('can visit subroutes', async function() {
+    await visit('/');
+
+    expect(find('h2').text()).to.be.empty;
+
+    await visit('/foo');
+
+    expect(find('h2').text()).to.be.equal('this is an acceptance test');
+  });
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-with-nested-andthen.js b/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-with-nested-andthen.js
new file mode 100644
index 0000000..0284af3
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-with-nested-andthen.js
@@ -0,0 +1,19 @@
+import Ember from 'ember';
+import { describe, it, beforeEach, afterEach } from 'mocha';
+
+const { expect } = window.chai;
+
+describe('basic acceptance test', function() {
+  it('can click save', async function() {
+    await visit('/');
+
+    let saveButton = find('button:contains("Save")');
+
+    expect(find('h2').text()).to.be.empty;
+    await fillIn('.field', 'value');
+
+    expect(saveButton).to.be.enabled;
+    await click(saveButton);
+    expect(saveButton).to.be.disabled;
+  });
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-without-done.js b/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-without-done.js
new file mode 100644
index 0000000..3852f50
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/new-acceptance-test-without-done.js
@@ -0,0 +1,16 @@
+import Ember from 'ember';
+import { describe, it, beforeEach, afterEach } from 'mocha';
+
+const { expect } = window.chai;
+
+describe('basic acceptance test', function() {
+  it('can visit subroutes', async function() {
+    await visit('/');
+
+    expect(find('h2').text()).to.be.empty;
+
+    await visit('/foo');
+
+    expect(find('h2').text()).to.be.equal('this is an acceptance test');
+  });
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/new-qunit.js b/tests/fixtures/convert-tests-to-async-await/new-qunit.js
new file mode 100644
index 0000000..319e3fb
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/new-qunit.js
@@ -0,0 +1,15 @@
+import { test } from 'qunit';
+import moduleForAcceptance from '../../tests/helpers/module-for-acceptance';
+import { currentURL, find, findAll, click, visit } from 'ember-native-dom-helpers';
+
+moduleForAcceptance('Acceptance | helpers | selectChoose');
+
+test('selectChoose helper opens the select and selects the option with the given text', async function(assert) {
+  await visit('/helpers-testing');
+
+  assert.equal(currentURL(), '/helpers-testing');
+  selectChoose('.select-choose', 'three');
+  assert.equal(find('.select-choose .ember-power-select-trigger').textContent.trim(), 'three', 'The proper value has been selected');
+  assert.notOk(find('.ember-power-select-options'), 'The selectis closed');
+  assert.equal(find('.select-choose-target').textContent.trim(), 'You\'ve selected: three');
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-with-callback.js b/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-with-callback.js
new file mode 100644
index 0000000..446c82e
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-with-callback.js
@@ -0,0 +1,26 @@
+import Ember from 'ember';
+import { describe, it, beforeEach, afterEach } from 'mocha';
+
+const { expect } = window.chai;
+
+describe('basic acceptance test', function() {
+  it('can visit subroutes', function(done) {
+    visit('/');
+
+    andThen(function() {
+      expect(find('h2').text()).to.be.empty;
+    });
+
+    visit('/foo');
+
+    andThen(function() {
+      expect(find('h2').text()).to.be.equal('this is an acceptance test');
+    });
+
+    server.put('/contacts', (db, request) => {
+      let params = JSON.parse(request.requestBody).contact;
+      assert.deepEqual(params, {});
+      done();
+    });
+  });
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-with-done.js b/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-with-done.js
new file mode 100644
index 0000000..e2e884d
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-with-done.js
@@ -0,0 +1,21 @@
+import Ember from 'ember';
+import { describe, it, beforeEach, afterEach } from 'mocha';
+
+const { expect } = window.chai;
+
+describe('basic acceptance test', function() {
+  it('can visit subroutes', function(done) {
+    visit('/');
+
+    andThen(function() {
+      expect(find('h2').text()).to.be.empty;
+    });
+
+    visit('/foo');
+
+    andThen(function() {
+      expect(find('h2').text()).to.be.equal('this is an acceptance test');
+      done();
+    });
+  });
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-with-nested-andthen.js b/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-with-nested-andthen.js
new file mode 100644
index 0000000..7678bbd
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-with-nested-andthen.js
@@ -0,0 +1,27 @@
+import Ember from 'ember';
+import { describe, it, beforeEach, afterEach } from 'mocha';
+
+const { expect } = window.chai;
+
+describe('basic acceptance test', function() {
+  it('can click save', function(done) {
+    visit('/');
+
+    andThen(function() {
+      let saveButton = find('button:contains("Save")');
+
+      expect(find('h2').text()).to.be.empty;
+      fillIn('.field', 'value');
+
+      andThen(function() {
+        expect(saveButton).to.be.enabled;
+        click(saveButton);
+      });
+
+      andThen(function() {
+        expect(saveButton).to.be.disabled;
+        done();
+      });
+    });
+  });
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-without-done.js b/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-without-done.js
new file mode 100644
index 0000000..e2f6ddd
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/old-acceptance-test-without-done.js
@@ -0,0 +1,20 @@
+import Ember from 'ember';
+import { describe, it, beforeEach, afterEach } from 'mocha';
+
+const { expect } = window.chai;
+
+describe('basic acceptance test', function() {
+  it('can visit subroutes', function() {
+    visit('/');
+
+    andThen(function() {
+      expect(find('h2').text()).to.be.empty;
+    });
+
+    visit('/foo');
+
+    andThen(function() {
+      expect(find('h2').text()).to.be.equal('this is an acceptance test');
+    });
+  });
+});
diff --git a/tests/fixtures/convert-tests-to-async-await/old-qunit.js b/tests/fixtures/convert-tests-to-async-await/old-qunit.js
new file mode 100644
index 0000000..955f778
--- /dev/null
+++ b/tests/fixtures/convert-tests-to-async-await/old-qunit.js
@@ -0,0 +1,20 @@
+import { test } from 'qunit';
+import moduleForAcceptance from '../../tests/helpers/module-for-acceptance';
+import { currentURL, find, findAll, click, visit } from 'ember-native-dom-helpers';
+
+moduleForAcceptance('Acceptance | helpers | selectChoose');
+
+test('selectChoose helper opens the select and selects the option with the given text', function(assert) {
+  visit('/helpers-testing');
+
+  andThen(function() {
+    assert.equal(currentURL(), '/helpers-testing');
+    selectChoose('.select-choose', 'three');
+  });
+
+  andThen(function() {
+    assert.equal(find('.select-choose .ember-power-select-trigger').textContent.trim(), 'three', 'The proper value has been selected');
+    assert.notOk(find('.ember-power-select-options'), 'The selectis closed');
+    assert.equal(find('.select-choose-target').textContent.trim(), 'You\'ve selected: three');
+  });
+});