Ember CLI - the Ramcat way

There’s not a ton of good reasons to do anything to the index.html file in CLI. Exceptions include CDN links that you don’t want concatenated into your CSS file. Any application-wide stuff is going to go in your application.hbs or index.hbs file. If you want headers and footers, those are often included as partials in either of those files.

As for tests for existence, those are the default tests when you generate a page with ember g whatever name. They’re helpful for letting you know if a file was deleted. As for having an initial failing test for the existence of a page, that falls under the “don’t test the framework” mantra. ember g route my-route will give you everything you need and wire it up correctly- that’s what ember-cli’s own tests are for.

Should you test that a link is displaying? If it’s bound by some logic (“only display if this is true”), I would say yes. Otherwise, trust that if you put a {{#link-to}} helper in the template, it will do its job. From an acceptance test standpoint, if the user needs to click on the link to accomplish some task, you’ll end up testing that the link is there as a part of that process.

EDIT: messed up the generator name format

2 Likes

Yup, should have watched the video before posting. That is the TDD mantra I was looking for. I’ll have to watch it a few times because I can’t see some of the changes he’s making. Like the styling he added to prove the designer wouldn’t break his tests - did he copy in a stylesheet, copy the project to another folder and rebuild, I don’t know command line is so fast and its been a decade since I even looked at vim (maybe two).

I think that is where I am struggling, there where no tests generated with “ember new app-name” so I have pages without tests, and haven’t yet used the page generator myself and seen it generate tests. Also I have no index.hbs, does that page not get generated on “new”?

index.hbs doesn’t come by default. Use ember generate route index and you’ll get a route, a template, and a test.

Ah, there is a key piece here that I just got from you. Unlike testing in Client applications (C#/Java), where you create the test class first, in CLI you generate the target page and the test at the same time. I think that needs to be part of the documentation (for beginner’s to framework created tests).

I am also really confused how application.hbs exists without a test - or rather why are there no generated tests at all with “ember new app-name”? No test for the existing route “/”. And by no tests I’m not discounting the JSHint tests - I don’t know how they get there in code, but I think they are a format checker for JavaScript.

As I am writing this I think I can see some of the thought behind having no tests generated with the basic application. The basic application is the framework (which we don’t test…).

OK, that gives me this thought - the Guide to testing starts with - here is how you generate a test “ember generate acceptance-test user-can-login”, shouldn’t this really start with something a beginner would need…

How about, here is an acceptance test that puts a link on a page and allows you to move to a second page in your application?

You’re technically right, but you might be missing the spirit of it. You can also do it the way you’re used to by manually creating the unit test first. You don’t have to use generators, they’re just shortcuts (one CLI command vs. 3 file creation tasks, plus adding boilerplate). The idea behind creating 3 files off of the route generator, for example, is that you start by recognizing you need a new route, there’s not much reason to have a route without a template, and the test is generated to remind you that you should test your code. Whether you start by writing your test specs (TDD) or by writing your code (DHH) is up to the developer.

The basic application is, in fact, the framework. Without an application.hbs file, you don’t have an ember-cli application. Your application route also doesn’t have a test, because it works by default without any custom code. If you start adding custom behavior to the application route, you should generate a test for it. You don’t have an application controller by default because nothing in the application.hbs file is bound to anything, ergo, no need to include an application controller test by default.

As for your guide idea, I think it’s a great one. In fact, I think it would kind of cool to have a bunch of test recipes along with the code for the cookbook.

There are not enough days/weeks/months to write all the thoughts I have so I’ll do the best I can with my limited human abilities/faults.

#1

I think this is wrong because wouldn’t that just be testing the framework? This is tied to the next thought, see below.

#2

That’s the second time you’ve said that to me - and I’ve searched long and hard to find out what you mean. The best I can come up with is… a rather long explanation… but summed up in the phrase “coding with intent”. Let me explain what that means to me. When coding in Java and in a controller and I want to access some property/method on another class I simply write out that name of the method I want. If it won’t compile it shows intent that I want that other object to have that property or method. I then go create or modify a test to begin to realize that intent and finally add the method to the other object. Once my test is green I go back to working on the controller who will now compile.

Remember I said this is a long explanation, as I have been using CLI, I have encountered the feeling of Coding-With-Intent by using the generators. When you write “ember g route route-name” you’re declaring a really big intent of having a “route-name” page in your application.

@kylecoberly What else might I be missing the spirit of (as obvious by my (hopefully passing) clash of culture with Ember-CLI methodology my “missings” may be huge)?

#3 OK, I’m a little stuck. In my old Ember application I used the concept of a “Fixture” to load data from the local storage. My route did this:

App.CollectionRoute = Ember.Route.extend({
  model: function() {
    return loadCollectionData();
  }
});

My fixture was:

// Fixtures
var collectionData = {collection: [], genes: []};

The method that loaded the fixture (and was called in the route) was:

function loadCollectionData() {
  var StoredData = localStorage.getItem('Collection');
  if (StoredData)
  {
    StoredData = JSON.parse(StoredData);
    collectionData = {collection: [], genes: []};
    var Collection = StoredData['collection'];
    for (var Index in Collection)
    {
      // Ember bug protection
      if (isNumber(Index))
      {
        var aSnake = App.Snake.create({name: Collection[Index]['name'], sex: Collection[Index]['sex'], yearAcquired: 
          Collection[Index]['yearAcquired'], yearBorn: Collection[Index]['yearBorn'], image: Collection[Index]['image']});
        // genetics: [],
        var Loci = Collection[Index]['genetics'];
        Genes = Ember.A();
        for (var Index in Loci)
        {
          // Ember bug protection
          if (isNumber(Index))
          {
            Genes.pushObject(App.Locus.create({allele1: Loci[Index]['allele1'], allele2: Loci[Index]['allele2'],
              type: Loci[Index]['type'], complex: Loci[Index]['complex']}));
          }
        }
        aSnake.set('genetics', Genes);
        collectionData['collection'].pushObject(aSnake);
      }
    }
  }

  return collectionData;
}

What is the CLI version of “Fixtures”?

Edit: #4

In my previous app I used a number of objects, where do I create those in CLI? Like:

App.Gene = Ember.Object.extend({
	name: null,
	type: null,
	complex: null
});

App.Snake = Ember.Object.extend({
	id: null,
	name: null,
	sex: null,
	yearAcquired: null,
	yearBorn:  null,
	genetics: [],
	image: null,
	
	morph: function() {
		var Genes = this.get('genetics');
		if (Genes.get('length') > 0)
		{
			var Result = "";
			for (var Index in Genes)
			{
				if (isNumber(Index)) // Leaking properties bug
				{
					Result += Genes[Index].get('morph') + " ";
				}
			}
			return Result;
		}
		else
		{
			return "Normal";
		}
	}.property('genetics.[]'),
	
	age: function() {
		if (this.get('yearBorn') == null)
		{
			return 0;
		}
		
		var aDate = new Date();
		return aDate.getFullYear() - this.get('yearBorn');
	}.property('yearBorn')
});

#5 I tried “ember g model model-name” and the model was created as an Ember-Data model. I’m not using Ember-Data, how do I fix that?

I think that’s more of a concern with unit testing. If it’s a crucial workflow I think it’s reasonable to have an acceptance test for it, although I could see the counter-argument.

The convention over configuration philosophy permeates just about everything in Ember. Instead of making a component, then a template, then a test as you need them, the generator makes all three because that’s the workflow you’ll use 99% of the time. You can still do red/green/refactor testing as you’re describing, you just get all of the objects wired up for you already off the generator. You can also do exactly the coding-with-intent method you’re describing and add each piece one at a time, which is useful for corner cases. If you’re doing that all the time though, you’re missing one of the major ease-of-use and speed advantages Ember offers.

Fixtures still exist in CLI, and work the same way. The syntax for declaring them is slightly different, however. If you’re trying to do this outside of Ember Data, I’m not sure what the best practices are. I will say to anyone else reading this, that ED fixtures are unofficially deprecated in favor of http-mocks. This approach allows you to use the actual adapter you want to use, and use a stubbed out endpoint to return data.

I was actually wondering this myself today. Almost all of my stuff comes from Ember Data models, but I need a basic class right now and I don’t see a CLI generator for it. With that in mind, ES6 modules are super flexible. You can just declare your object in a module anywhere in your app structure, import it into whatever files want to use it, and then it’s just regular 'ole javascript. With the caveat that I haven’t tested this, maybe something like this:

// app/objects/gene.js
export default Ember.Object.extend({
   name: null,
   type: null,
   complex: null
});

// app/organism/controller.js
import Gene from '../../objects/gene';
someProperty: function(){
   var myGene = new Gene;
   myGene.type = "blahblahblah";
   return myGene;
}

The model generator is for Ember Data. If you’re using Ember Model or a roll-your-own, you can make your own blueprint, or use the import-a-class method from above.

There are some mental shifts to doing Ember via CLI (yeah, understatement). And having said that, I’m not sure I know how to set up a global variable the way I used too in App.js in the way Ember was documented when I started (which was non-CLI). What I was doing with that “fixture” was loading data from local storage from several routes (and perhaps from some controllers but I would have to look). That data was the model for the “collection” page.

The second part is something I know how to do in other languages but haven’t figured it out in Ember-CLI. I want to create an adapter to load local storage so that I can stub the adapter to provide test data. Because having now gotten my “collection” route to load local storage I now no longer have my test data for my tests.

I have this unit test: factory-test.js

import {module, test} from 'qunit';
import Factory from '../../../objects/factory';
import DataFacade from '../../../objects/datafacade';
import MockDataFacade from '../helpers/mockdatafacade';

module('factory');

test('test we can swap a mock object for the DataFacade', function(assert) {
/* code removed from post because it is currently commented out */
});

And this factory.js

import Ember from 'ember';
import DataFacade from 'datafacade';

export default Ember.Object.extend({
  dataFacadeObject: null,
  
  setDataFacade: function(facade) {
    this.set('dataFacadeObject', facade);
  },
  dataFacade: function() {
    if (this.get('dataFacadeObject') == null)
    {
          return DataFacade.create({});
    }
    else
    {
      return this.get('dataFacadeObject');
    }
  }
});

The error I get is:

TestLoader Failures: code/tests/unit/objects/factory-test: could not be loaded (1, 0, 1)Rerun2 ms1.Died on test #1 at TestLoader.prototype.moduleLoadFailure (http://localhost:4200/assets/test-support.js:5539:5) at TestLoader.prototype.require (http://localhost:4200/assets/test-loader.js:31:9) at TestLoader.prototype.loadModules (http://localhost:4200/assets/test-loader.js:21:13) at TestLoader.load (http://localhost:4200/assets/test-loader.js:40:7) at Anonymous function (http://localhost:4200/assets/test-support.js:5545:5): Could not find module datafacade imported from code/objects/factory@ 1 msSource: Error: Could not find module datafacade imported from code/objects/factory at requireFrom (http://localhost:4200/assets/vendor.js:119:7) at reify (http://localhost:4200/assets/vendor.js:106:9) at Anonymous function (http://localhost:4200/assets/vendor.js:149:7) at tryFinally (http://localhost:4200/assets/vendor.js:30:7) at requireModule (http://localhost:4200/assets/vendor.js:148:5) at requireFrom (http://localhost:4200/assets/vendor.js:121:5) at reify (http://localhost:4200/assets/vendor.js:106:9) at Anonymous function (http://localhost:4200/assets/vendor.js:149:7) at tryFinally (http://localhost:4200/assets/vendor.js:30:7) at requireModule (http://localhost:4200/assets/vendor.js:148:5)

For completeness here is datafacade-test.js:

import {module, test} from 'qunit';
import DataFacade from '../../../objects/datafacade';

module('datafacade');

test('test we can load all the snakes in a collection', function(assert) {
  var aFacade = DataFacade.create({});
  var Collection = aFacade.loadCollection();
  
  assert.equal(Collection, Collection);
});

And datafacade.js:

import Ember from 'ember';

export default Ember.Object.extend({
  loadCollection: function() {
    return 'real data';
  }
});

Why can’t the test file load?

Ok, I’ve found the gist of the problem. As soon as I force (via TDD) the factory.cs file to return a DataFacade object from the loadCollection method the factory-test.js won’t load. Is there some ‘using’ statement or something I am missing to tell factory-test.js that there is a dependency on DataFacade as well (imports don’t seem to work)?

So I could not get the above problem to work, so I have been reading the Ember docs all weekend. I rewrote my factory as a service. That seemed to work I could expose a DataFacade object from the PrimeFactory service (controller) and I could swap it out so I could provide test data to my tests. Unfortunately as soon as I tried to make the datafacade-test.js expose/load my snake object my test file would no longer load.

This is my import:

import Snake from '../../../app/objects/snake';

The error I get:

Error: Could not find module `code/app/objects/snake` imported from `code/tests/unit/controllers/datafacade-test`

The path is right but does not load.

Can CLI use custom objects? Or does everything have to be Ember-Data?

My app stores data on the localStorage only and I created a façade (then replaced that with a service) to wrap that (that being the act of storing on localStorage) so I could swap out the implementation that actually loads the data from local storage. Thus providing stub/fake/controlled data for my tests.

So am I doing something wrong?

The really short answer is yes! Ember data is completely optional.

I wrote a localStorage wrapper that abstracts it to a service, but is not an EmberData Adapter. I wrote it for an app I’m making that is frontend only, no upstream data storage.

https://github.com/mfeckie/ember-cli-local-storage-wrapper

Ok, I followed the link but most of the directories are empty. I can’t find any files with code in them. I assume I’m doing it wrong but I can’t figure out how. I pulled down the zip file and it also was mostly empty folders.

I don’t have a github account so maybe the code files aren’t public?

Nope, You are quite right! I’ve published to NPM but forgot to push to Github. Will push it when I get home as it’s one a different machine. Should be visible ~12 hours from now. Apologies for sending you down an incorrect path.

I’ve pushed the repo now, apologise for earlier :smile:

Awesome. I’ve looked at it but haven’t had time to play with it yet. It looks like the bulk of it is contained within three files. The import export trick of the file in the app directory might be the trick I need to get my tests to load my custom objects.

Also Ember.Service I haven’t seen in the Guides, I found it in the API documentation though, will have to look into that.

@Ramcat unfortunately the ember service stuff is not very well documented in the guides yet. There was a blog post that talked about it briefly when ember 1.10 was released

I put together a very minimal ember cli example that shows how a service is created and how to inject it (in this case into a component).

Hopefully this is useful to you.

Thanks for those links. Glad to see services with a different class name than controllers. When I said before I had re-written my DataFacade as a service I meant the old style lightweight service, I did not know about the new services. I usually work on Ember during the weekend but there is so much to do this weekend that I may not get to it. Soon though.

Ok, so I took the plunge and ran “npm install ember-cli-local-storage-wrapper”. I thought that since I did not use the global flag it would install in my project. It didn’t, and the only reference on my machine I could find was “C:\Users[User Name]\AppData\Roaming\npm-cache\ember-cli-local-storage-wrapper” but no code there.

I know I need to inject the service into a controller, but I was more hoping to have the code in the project so I could try to modify it or use it as an example to write parallel code to write my own service that would load my custom objects.

At present I’m stuck on how to use your package. Since it seems to just be a few files, maybe the easy way is to just copy them manually into my project and go from there.