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 returnthis
. 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 theirbootstrap()
callback. I suggest adding a new methodsetupContainer()
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 thedone
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 myassert
(because of the way aurelia uses theTaskQueue
which is processed later than thethen
s ofPromises
. A thenableready
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).