import { moduleFor } from 'ember-qunit';
import test from 'ember-sinon-qunit/test-support/test';
import wait from 'ember-test-helpers/wait';
import Service from 'ember-service';

let service;
let StubNotifyService = Service.extend({
  error() { }
});

moduleFor('service:interactivity', 'Unit | Service | interactivity', {
  beforeEach() {
    service = this.subject();
    this.register('service:notify', StubNotifyService);
    this.inject.service('notify');
  },
  afterEach() {
  }
});

test('interactivity service tracks rendered interactive components', function (assert) {
  service.reportInteractive('foo-bar');
  assert.equal(service._interactiveComponents['foo-bar'], true, 'service knows about component');
  service.reportNonInteractive('foo-bar');
  assert.equal(service._interactiveComponents['foo-bar'], undefined, 'service does not know about component');
});

test('interactivity service monitors for page interactive criteria expressed as criticalComponents and isInteractive', function (assert) {
  assert.expect(2);
  let done = assert.async();
  let completed = false;
  let criticalComponents = ['foo-bar-1', 'foo-bar-2'];
  let options = {
    criticalComponents: criticalComponents,
    isInteractive(interactiveComponents) {
      return criticalComponents.every((name) => {
        return interactiveComponents[name];
      });
    }
  };

  service.reportInteractive('foo-bar-1');
  service.monitor(options).then(() => {
    completed = true;
    assert.ok(completed, 'service triggered interactive when all conditions were met');
    done();
  });

  assert.notOk(completed, 'service did not trigger interactive before all conditions are met');
  service.reportInteractive('foo-bar-2');
});

test('interactivity service will notify the developer if the route never becomes interactive in development', function (assert) {
  assert.expect(2);
  let MOCK_TIMEOUT = 1000;
  let done = assert.async();
  let options = {
    criticalComponents: [],
    isInteractive() {
      return false; // never becomes interactive
    }
  };

  let notifyStub = this.stub(service.get('notify'), 'error');

  service.set('_devEnvTimeoutMiliseconds', MOCK_TIMEOUT);
  service.set('_env', { environment: 'development' });
  service.monitor(options);

  assert.notOk(notifyStub.calledOnce, 'service did not notify developer before the timeout');

  wait().then(() => {
    assert.ok(notifyStub.calledOnce, 'service did notify developer that the route never became interactive');
    done();
    return wait();
  });

});

test('interactivity service will not notify the developer in production', function (assert) {
  assert.expect(2);
  let MOCK_TIMEOUT = 1000;
  let done = assert.async();
  let options = {
    criticalComponents: [],
    isInteractive() {
      return false; // never becomes interactive
    }
  };
  let notifyStub = this.stub(service.get('notify'), 'error');

  service.set('_devEnvTimeoutMiliseconds', MOCK_TIMEOUT);
  service.set('_env', { environment: 'production' });
  service.monitor(options);

  assert.notOk(notifyStub.calledOnce, 'service did not notify developer before the timeout');

  wait().then(() => {
    assert.notOk(notifyStub.calledOnce, 'service did not notify developer after the timeout');
    done();
    return wait();
  });

});

test('interactivity service will not notify the developer in test', function (assert) {
  assert.expect(2);
  let MOCK_TIMEOUT = 1000;
  let done = assert.async();
  let options = {
    criticalComponents: [],
    isInteractive() {
      return false; // never becomes interactive
    }
  };
  let notifyStub = this.stub(service.get('notify'), 'error');

  service.set('_devEnvTimeoutMiliseconds', MOCK_TIMEOUT);
  service.monitor(options);

  assert.notOk(notifyStub.calledOnce, 'service did not notify developer before the timeout');

  wait().then(() => {
    assert.notOk(notifyStub.calledOnce, 'service did not notify developer after the timeout');
    done();
    return wait();
  });

});
