aurelia / testing

Simplifies the testing of UI components by providing an elegant, fluent interface for arranging test setups along with a number of runtime debug/test helpers.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

A bit of early feedback on ComponentTester

sylvain-hamel opened this issue · comments

I tried ComponentTester, it's looking great. I have a bit of feedback:

  • The bootstrap() method should return this. usage:
    component = new ComponentTester()
      .bootstrap((aurelia) => {
        aurelia.use.standardConfiguration();
        container = aurelia.container;
        let foo = jasmine.createSpyObj('foo', ['bar']);
        container.registerInstance(Foo, foo);
      })
      .withResources('foo')

  • I feel like most people will have to call aurelia.use.standardConfiguration(); in their bootstrap() callback. I suggest adding a new method setupContainer() so that we can use this instead:
    component = new ComponentTester()
      .setupContainer((container) => {
        let foo = jasmine.createSpyObj('foo', ['bar']);
        container.registerInstance(Foo, foo);
      })
      .withResources('foo')
  • In order to assert correct component behavior, it is necessary to access the viewModel. I would add a viewModel() method like this:
  viewModel(){
    return this.element.au.controller.viewModel;
  }

usage:

    component.create()
      .then(() => {
        expect(component.viewModel().value).toEqual(10);
      })
      .then(done);
  • Add a dispose() method that takes the done callback. This would cleanup everything and end the test. usage:
    component.create()
      .then(() => {
        expect(...);
      })
      .then(()=>component.dispose(done));

Great feedback. What is in the repo now is basically just an "idea" implementation. We've got a bunch more we need to do on it. This will help us in refining the API. Thanks!

More feedback:

  • in order to assert the state of the DOM I have to use setTimeout in my assert (because of the way aurelia uses the TaskQueue which is processed later than the thens of Promises. A thenable ready would be useful:
    component.create().then(() => {
        component.ready.then(() => {
          expect($(component.element).text()).toEqual('foo');
        });
      })

I don't like how this chains but you get the idea...

It's not done yet, so that's not how it would work. See our latest blog post for a sample of the api we are thinking about: http://blog.durandal.io/2016/02/01/big-february-status-update/

More feedback

  • withResources() could also support constructor(s) (instead of strings). This way the tests can import the type(s) and load them from the types themselves.
  import {Foo} from '../src/foo'
  ...
  component = new ComponentTester()
      .withResources(Foo)

until then I use:

    component = new ComponentTester()
      .withResources(Origin.get(Foo).moduleId)

Maybe...it's a bit tricky because the Origin can be somewhat unreliable depending on how yo import the module. We could implement basically what you have above...it should work most of the time, but there could be times when it wouldn't work.

More feedback

I ran onto a timing issue. The element I wanted to test has a dependency on Show (show.bind). Running the test gave an error about DI not being able to satisfy Show's dependency on element.

Under normal use in a real app, the <script> in index.html makes it so that aurelia.run() is executed before all the imports are loaded and that's where aurelia-pal-browser is initialized.

But karma loads all spec files and their dependencies way before ComponentTester initializes its own aurelia instance. So Show is loaded very early and cannot resolve _aureliaPal.DOM.Element.

My solution, was to add initialize-pal-browser.js as the first file of my karma.config:

  jspm: {
      loadFiles: ['test/initialize-pal-browser.js', 'src/**/*.js', 'test/**/*.js'],

and that file content is :

import {initialize} from 'aurelia-pal-browser';
initialize();

@EisenbergEffect I just want to let you know that we have written several unit tests with ComponentTester and it works pretty well. I don't think there is much more to do to make a first release.

We extended ComponentTester to simplify the api and make it easy to pass in mocks. A typical integrated unit test now looks like this:

  it('renders lookup value', done => {

    MyComponentTester.load({
      type: LookupText,
      mocks: container=> {
        let lookupService = jasmine.createSpyObj('lookupService', ['getLookupItemText']);
        lookupService.getLookupItemText.and.returnValue(Promise.resolve('myLookup1'));
        container.registerInstance(LookupService, lookupService);
      },
      markup: '<lookup-text source-type.bind="sourceType" source.bind="source" value.bind="value"></lookup-text>',
      setupBindingContext: (context)=> {
         Object.assign(context, {value: 10, sourceType: 'lookup', source: 'common/users'});
      }
    }).then(component=> {
      let renderedValue = component.$el.find('.text').text();
      expect(renderedValue).toEqual('myLookup1');
      component.dispose(done);
    });

  });

Let me know if you want any help to release something based on that.

Did you introduce jQuery?

@EisenbergEffect only in my extension (MyComponentTester) and only in order to expose $el.

so instead of

let renderedValue = $(component.element).find('.text').text();

we can write

let renderedValue = component.$el.find('.text').text();

I'd keep this in my own subclass not in ComponentTester itself

I have also started working on aurelia specific matchers such as

    let dropdown = component.$el.find('dropdown');
    expect(dropdown).toBeVisible();

where toBeVisible() checks for the presence of the aurelia-hide class in addition to looking if the element is :visible or if it is non-existent (as it is the case when using if.bind).

That sounds very interesting. Let's get our basics in place and then maybe we can consider adding some things like that too. It sounds very convenient and we really want to make it easy for developers to test components (without having to do full e2e testing).

@EisenbergEffect is there any timeline. seems rather helpful as it is already (though was a bit tricky to get it going without much of a guideline here 😄 )

It needs some significant work. What's here is mostly just a "reminder". We don't have anyone dedicated to working on it right now. I think @jdanyow had some ideas. He may be freed up to work on it soon. He's working on an alternative workflow using RequireJS right now. The whole JSPM/SystemJS issue has taken precedence.

uch, right. i agree. tx for now

The sample API from the Aurelia blog looks great. Is there any progress on this - or anything we can get involved/help with?

@sylvain-hamel do you have a version with your changes that you're able to share?

We'd be glad to have your help. @martingust I think you were looking into working on this for use with the ui-virtualization. Maybe we can get @axwalker helping on this effort?

@axwalker I'll have something to share very soon (a PR and a blog post).