Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Store/Coordinator test support. #155

Open
ghost opened this issue Jan 15, 2018 · 5 comments
Open

Store/Coordinator test support. #155

ghost opened this issue Jan 15, 2018 · 5 comments

Comments

@ghost
Copy link

ghost commented Jan 15, 2018

I've been hacking around with setting up and tearing down Orbit around (acceptance) tests. More specifically I'm building a demo app that only use IDB as backup and no remote JSON:API source.

To be able to access the store in an acceptance context I had to manually construct the whole store within the moduleForAcceptance helper.

The second thing is that it looks like when using Orbit's store like that, the acceptance test helpers don't pickup on the Promises that Orbit's actions create, so it will just not wait for them. I had to create a simple waitFor async helper that takes Orbit's promises and queues them up properly.

What I'm alluding to is that it would be awesome if Ember-Orbit contained a few test helpers that stream lines the test experience.

Below is part of the Orbit setup I wrote for the tests:

example acceptance test

test('It displays existing planets', function(assert) {
  waitFor(() => this.store.update((t) => [
    t.addRecord({ type: 'planet', attributes: { name: 'Mars'} }),
    t.addRecord({ type: 'planet', attributes: { name: 'Venus'} })
  ]));

  visit('/planets');

  andThen(() => {
    assert.dom('[data-test-planet]').exists({ count: 2 });
  });
});

tests/helpers/module-for-acceptance.js

import { module } from 'qunit';
import { resolve, all } from 'rsvp';
import startApp from '../helpers/start-app';
import destroyObject from '../helpers/destroy-app';
import createOrbitStore from './create-orbit-store';

export default function(name, options = {}) {
  module(name, {
    beforeEach() {
      this.application = startApp();

      return createOrbitStore(this.application)
        .then(([store, coordinator]) => {
          this.store = store;
          this.coordinator = coordinator;
        })
        .then(() => options.beforeEach && options.beforeEach.apply(this, arguments));
    },

    afterEach() {
      let afterEach = options.afterEach && options.afterEach.apply(this, arguments);
      return resolve(afterEach)
        .then(() => {
          let backup = this.coordinator.getSource('backup');
          let modelNames = Object.keys(this.store.source.schema.models);
          return all(modelNames.map((modelName) => backup.clearRecords(modelName)));
        })
        .then(() => this.coordinator.deactivate())
        .then(() => destroyObject(this.store))
        .then(() => destroyObject(this.application));
    }
  });
}

tests/helpers/create-orbit-store.js

import { get } from '@ember/object';
import { camelize } from '@ember/string';
import OrbitStore from '@orbit/store';
import Coordinator from '@orbit/coordinator';
import {
  Schema,
  KeyMap
} from '@orbit/data';
import modulesOfType from 'ember-orbit/-private/system/modules-of-type';
import { Store } from 'ember-orbit';

function findModulesOfType(application, type) {
  return modulesOfType(application.modulePrefix, type).map(camelize);
}

function createSchema(application) {
  let modelSchemas = {};
  let modelNames = findModulesOfType(application, 'models');

  modelNames.forEach(name => {
    let model = application.resolveRegistration(`model:${name}`);
    modelSchemas[name] = {
      id: get(model, 'id'),
      keys: get(model, 'keys'),
      attributes: get(model, 'attributes'),
      relationships: get(model, 'relationships')
    };
  });

  return new Schema({ models: modelSchemas });
}

function createSources(application, injections) {
  return findModulesOfType(application, 'data-sources').map((name) => {
    let sourceFactory = application.resolveRegistration(`data-source:${name}`);
    return sourceFactory.create(injections);
  });
}

function createStrategies(application) {
  return findModulesOfType(application, 'data-strategies').map((name) => {
    let strategyFactory = application.resolveRegistration(`data-strategy:${name}`);
    return strategyFactory.create();
  });
}

export default function createOrbitStore(application) {
  let schema = createSchema(application);
  let orbitStore = new OrbitStore({ schema });
  let keyMap = new KeyMap();
  let sources = createSources(application, { schema, keyMap });
  let strategies = createStrategies(application);
  let coordinator = new Coordinator({ sources: [orbitStore, ...sources], strategies });
  let store = Store.create({ source: orbitStore });

  return coordinator.activate()
    .then(() => [store, coordinator]);
}

tests/helpers/wait-for.js

import { registerAsyncHelper } from "@ember/test"

registerAsyncHelper('waitFor',
  function(app, callback) {
    return callback();
  }
);
@Turbo87
Copy link

Turbo87 commented Jan 16, 2018

IMHO any new test helpers should target the new QUnit testing APIs since they are massively easier to deal with

@ghost
Copy link
Author

ghost commented Feb 9, 2018

I also ended up having to close the IDB connection when the Ember Application is being torn down

import IndexedDBSource from '@orbit/indexeddb';

export default {
  create(injections = {}) {
    injections.name = 'backup';
    injections.namespace = 'smart-shopping-list';
    let source = new IndexedDBSource(injections);

    source.destroy = function() {
      return this.closeDB();
    };

    return source;
  }
};

@dgeb
Copy link
Member

dgeb commented Feb 9, 2018

@martndemus Apologies for a delayed response. I really appreciate your exploration here and support shipping test helpers as needed 💯

IMHO any new test helpers should target the new QUnit testing APIs since they are massively easier to deal with

@Turbo87 yeah, that is optimal 👍

I also ended up having to close the IDB connection when the Ember Application is being torn down

@martndemus Makes sense. We should collect testing tips per source and include them in the right package in the main orbit repo. Ideally we can reference these from ember-orbit to minimize duplication.

@clekstro
Copy link

@dgeb Thanks so much for this addon. I have backup/remote sources working as expected, but am having trouble fixing my previous acceptance tests (which use the newer test helpers and other popular testing addons e.g. ember-cli-mirage). Are there any other docs/example apps that showcase how to integrate this with a newer app's test suite?

@RobbieTheWagner
Copy link
Contributor

I'm also curious about the testing story here. It would be awesome to get things working with mirage or something similar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants