Skip to content

Store/Coordinator test support. #155

@ghost

Description

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();
  }
);

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions