INSIDE

Ember CLI Mirage

October 2015

@samselikoff

The Problem

1. Development

2. Testing

export default Ember.Route.extend({

  model() {
    return this.store.findAll('post');
  }

});

The Problem

export default Ember.Route.extend({

  model() {
    return [
      {id: 1, title: 'Rails is Omakase'},
      {id: 2, title: 'Who needs JavaScript?'}
    ];
  }

});

The Problem

export default Ember.Route.extend({

  model() {
    return this.store.findAll('post');
  }

});


// mirage/config.js
export default function() {

  this.get('/posts', () {
    return [
      {id: 1, title: 'Rails is Omakase'},
      {id: 2, title: 'Who needs JavaScript?'}
    ];
  });

}

Route handler

test('I can view the posts', function(assert) {
  visit('/posts');
  
  andThen(() => {
    assert.equal(find('ul.posts li').length, 2));
  });
});

Testing

  • FixtureAdapter

    • ED only

    • Doesn't test adapter layer

  • Mocks

Testing

test('I can view the posts', function(assert) {
  pretender.get('/posts', () => {
    let posts = [...];
    return [200, {}, JSON.stringify(posts)];
  });

  visit('/posts');
  
  andThen(() => {
    assert.equal(find('ul.posts li').length, 2));
  });
});

Testing

test('I can view the posts', function(assert) {
  visit('/posts');
  
  andThen(() => {
    assert.equal(find('ul.posts li').length, 2));
  });
});

// mirage/config.js
export default function() {

  this.get('/posts', () {
    return [
      {id: 1, title: 'Rails is Omakase'},
      {id: 2, title: 'Who needs JavaScript?'}
    ];
  });

}

Testing

// mirage/config.js
export default function() {

  this.get('/posts', (db, request) {
    return db.posts;
  });

}
test('I can view the posts', function(assert) {
  server.createList('post', 2);

  visit('/posts');
  
  andThen(() => {
    assert.equal(find('ul.posts li').length, 2));
  });
});

Dynamic data

Testing

// mirage/factories/post.js
export default Factory.extend({

  title(i) {
    return `Post ${i}`;
  }

});

Factories

// mirage/fixtures/posts.js
export default [
  {id: 1, title: 'Rails is Omakase'},
  {id: 2, title: 'Who needs JavaScript?'}
];

Fixtures

Today

Fixtures

Factories

Database

Route handler

f

{
  posts: [
    ...
  ]
}

JSON

Issues

  1. Formatting
  2. Relationships

}

Formatting

// mirage/config.js
export default function() {

  this.get('/posts', (db, request) {
    return db.posts;
  });

}
{
  blog_posts: [
    {id: 1, post_title: 'Title 1'},
    {id: 2, post_title: 'Title 2'}
  ]
}
{
  blog-posts: [
    {id: 1, post-title: 'Title 1'},
    {id: 2, post-title: 'Title 2'}
  ]
}

Formatting

// mirage/config.js
export default function() {

  this.get('/posts', (db, request) {
    return db.blogPosts;
       //  db.blog_posts    ?
       //  db['blog-posts'] ?
  });

}

Formatting

// mirage/factories/post.js
export default Factory.extend({

  // post-title?
  // post_title?
  postTitle(i) {
    return `Post ${i}`;
  }
  

});

Formatting

Answer: Serializers

// mirage/config.js
export default function() {

  this.get('/posts', (db, request) {
    return db.blogPosts;
  });

}

/*
{
  blog_posts: [
    {id: 1, post_title: 'Post 1'}
  ]
}
*/
// mirage/serializers/application.js
export default Serializer.extend({

  root: true,

  keyForAttribute(key) {
    return camelize(key);
  }

});

Relationships

// mirage/config.js
export default function() {

  this.get('/posts/:id', ['post', 'comments']);

}

Assumes `comment.post_id`

/*
{
  posts: [
    ...
  ],
  comments: [
    ...
  ]
}
*/

Relationships

// mirage/fixtures/posts.js
export default [
  {id: 1, title: 'Post 1'}
];

// mirage/fixtures/comments.js
export default [
  {id: 1, title: 'Comment 1', post_id: 1},
  {id: 2, title: 'Comment 2', post_id: 1}
];

Relationships

test('I can view the post and comments', function(assert) {
  let post = server.create('post');
  server.createList('comment', 3, {post_id: post.id});

  visit(`/posts/${post.id}`);
  
  andThen(() => {
    assert.equal(find('ul.comments li').length, 3));
  });
});

Relationships

Where should we store relationship information?

Relationships

Answer: ORM

// mirage/models/post.js
export default Model.extend({

  comments: Mirage.hasMany()

});

// mirage/models/comment.js
export default Model.extend({

  post: Mirage.belongsTo()

});
// mirage/config.js
export default function() {

  this.get('/posts/:id', (schema, request) {
    let id = request.params.id;
    
    return schema.post.find(id);
  });

  // or, just the shorthand:
  this.get('/posts/:id');

}

// mirage/seializers/post.js
export default Serializer.extend({

  relationships: ['comments']

});

Relationships

Answer: ORM

// mirage/factories/post.js
export default Factory.extend({

  title(i) {
    return `Post ${i}`;
  },

  withComments: trait({
    comments: hasMany()
  })

});
test('I can view the post and comments', function(assert) {
  server.create('post', ['withComments', 3]);

  visit(`/posts/${post.id}`);
  
  andThen(() => {
    assert.equal(find('ul.comments li').length, 3));
  });
});
// mirage/scenarios/default.js

export default function(server) {
  let author = server.create('author');
  let post = author.createPost({title: 'Rails is Omakase'});

  post.createComment();
}

Relationships

Answer: ORM

// mirage/config.js
export default function() {

  this.get('/posts');
  this.post('/posts');
  this.get('/posts/:id');
  this.put('/posts/:id');
  this.del('/posts/:id');

}

Tomorrow

Fixtures

Factories

Database

Route handler

f

{
  posts: [
    ...
  ]
}

JSON

Schema

Serializer

Mirage Codebase

Thanks!

ember-cli-mirage.com

@samselikoff

Inside Ember CLI Mirage

By Sam Selikoff

Inside Ember CLI Mirage

  • 3,908