import { module, test } from 'qunit';
import EmberRouter from '@ember/routing/router';
import Route from '@ember/routing/route';
import Service from '@ember/service';
import { Promise } from 'rsvp';
import {
  setupContext,
  setupApplicationContext,
  teardownContext,
  teardownApplicationContext,
  setApplication,
  click,
  visit,
  currentRouteName,
  currentURL,
  setResolver,
} from '@ember/test-helpers';
import hasEmberVersion from '@ember/test-helpers/has-ember-version';
import { setResolverRegistry } from '../helpers/resolver';
import hbs from 'htmlbars-inline-precompile';
import { assign } from '@ember/polyfills';
import App from '../../app';
import config from '../../config/environment';

const Router = EmberRouter.extend({ location: 'none' });
Router.map(function() {
  this.route('widgets');
  this.route('posts', function() {
    this.route('post', { path: ':post_id' });
  });
  this.route('links-to-slow');
  this.route('slow');
});

module('setupApplicationContext', function(hooks) {
  if (!hasEmberVersion(2, 4)) {
    return;
  }
  let isolatedApp;
  hooks.beforeEach(async function(assert) {
    const AppConfig = assign({ autoboot: false }, config.APP);
    // .extend() because initializers are stored in the constructor, and we
    // don't want to pollute other tests using an application created from the
    // same constructor.
    isolatedApp = App.extend({}).create(AppConfig);
    let resolver = isolatedApp.Resolver.create({
      namespace: isolatedApp,
      isResolverFromTestHelpers: true,
    });

    setResolver(resolver);
    setApplication(isolatedApp);

    setResolverRegistry({
      'router:main': Router,
      'template:application': hbs`
        <div class="nav">{{link-to 'posts' 'posts'}} | {{link-to 'widgets' 'widgets'}}</div>
        {{outlet}}
      `,
      'template:index': hbs`<h1>Hello World!</h1>`,
      'template:links-to-slow': hbs`{{#link-to 'slow' class="to-slow"}}{{/link-to}}`,
      'template:posts': hbs`<h1>Posts Page</h1>{{outlet}}`,
      'template:posts/post': hbs`<div class="post-id">{{model.post_id}}</div>`,
      'service:foo': Service.extend({ isFoo: true }),
      'route:posts/post': Route.extend({
        model(params) {
          return params;
        },
      }),
      'route:widgets': Route.extend({
        model() {
          throw new Error('Model hook error from /widgets');
        },
      }),
      'route:slow': Route.extend({
        model() {
          assert.step('model start');
          return new Promise(resolve => {
            setTimeout(() => {
              assert.step('model resolving');
              resolve();
            }, 50);
          });
        },
      }),
    });

    await setupContext(this);
    await setupApplicationContext(this);
  });

  hooks.afterEach(async function() {
    await teardownApplicationContext(this);
    await teardownContext(this);
  });

  test('can perform a basic template rendering', async function(assert) {
    await visit('/');

    assert.equal(currentRouteName(), 'index');
    assert.equal(currentURL(), '/');

    assert.equal(this.element.querySelector('.nav').textContent, 'posts | widgets');
    assert.equal(this.element.querySelector('h1').textContent, 'Hello World!');
  });

  test('can perform a basic template rendering for nested route', async function(assert) {
    await visit('/posts/1');

    assert.equal(currentRouteName(), 'posts.post');
    assert.equal(currentURL(), '/posts/1');

    assert.equal(this.element.querySelector('.nav').textContent, 'posts | widgets');
    assert.equal(this.element.querySelector('.post-id').textContent, '1');
  });

  test('can visit multiple times', async function(assert) {
    await visit('/posts/1');

    assert.equal(currentRouteName(), 'posts.post');
    assert.equal(currentURL(), '/posts/1');

    assert.equal(this.element.querySelector('.nav').textContent, 'posts | widgets');
    assert.equal(this.element.querySelector('.post-id').textContent, '1');

    await visit('/');

    assert.equal(currentRouteName(), 'index');
    assert.equal(currentURL(), '/');

    assert.equal(this.element.querySelector('.nav').textContent, 'posts | widgets');
    assert.equal(this.element.querySelector('h1').textContent, 'Hello World!');

    await visit('/posts/2');

    assert.equal(currentRouteName(), 'posts.post');
    assert.equal(currentURL(), '/posts/2');

    assert.equal(this.element.querySelector('.nav').textContent, 'posts | widgets');
    assert.equal(this.element.querySelector('.post-id').textContent, '2');
  });

  test('can navigate amongst routes', async function(assert) {
    await visit('/');

    assert.equal(currentRouteName(), 'index');
    assert.equal(currentURL(), '/');

    await click('a[href="/posts"]');

    assert.equal(currentRouteName(), 'posts.index');
    assert.equal(currentURL(), '/posts');

    assert.equal(this.element.querySelector('h1').textContent, 'Posts Page');
  });

  test('bubbles up errors', function(assert) {
    assert.rejects(visit('/widgets'), /Model hook error from \/widgets/);
  });

  test('visit waits for async in model hooks', async function(assert) {
    assert.step('visiting');
    let visitPromise = visit('/slow');
    assert.step('after visit invocation');

    await visitPromise;

    assert.step('after visit resolved');

    assert.equal(currentRouteName(), 'slow');
    assert.equal(currentURL(), '/slow');

    assert.verifySteps([
      'visiting',
      'after visit invocation',
      'model start',
      'model resolving',
      'after visit resolved',
    ]);
  });

  test('settled waits for async in model hooks after a click', async function(assert) {
    await visit('/links-to-slow');

    assert.step('before click');
    let clickPromise = click('.to-slow');
    assert.step('after click');

    await clickPromise;

    assert.step('after click resolved');

    assert.equal(currentRouteName(), 'slow');
    assert.equal(currentURL(), '/slow');

    assert.verifySteps([
      'before click',
      'after click',
      'model start',
      'model resolving',
      'after click resolved',
    ]);
  });
});

@@ -297,26 +297,8 @@
 xt,%0A
-  setApplication,%0A
   cl
@@ -302,16 +302,16 @@
  click,%0A
+
   visit,
@@ -350,24 +350,23 @@
 L,%0A  set
-Resolver
+Context
 ,%0A%7D from
@@ -566,127 +566,8 @@
 le';
-%0Aimport %7B assign %7D from '@ember/polyfills';%0Aimport App from '../../app';%0Aimport config from '../../config/environment';
 %0A%0Aco
@@ -919,27 +919,9 @@
   %7D%0A
-  let isolatedApp;
 %0A
+
   ho
@@ -964,491 +964,8 @@
 ) %7B%0A
-    const AppConfig = assign(%7B autoboot: false %7D, config.APP);%0A    // .extend() because initializers are stored in the constructor, and we%0A    // don't want to pollute other tests using an application created from the%0A    // same constructor.%0A    isolatedApp = App.extend(%7B%7D).create(AppConfig);%0A    let resolver = isolatedApp.Resolver.create(%7B%0A      namespace: isolatedApp,%0A      isResolverFromTestHelpers: true,%0A    %7D);%0A%0A    setResolver(resolver);%0A    setApplication(isolatedApp);%0A%0A
     
@@ -2047,24 +2047,46 @@
 ),%0A    %7D);%0A%0A
+    setContext(this);%0A
     await se

Remove shared state work around.