New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support component integration tests #38

Merged
merged 1 commit into from May 3, 2015

Conversation

Projects
None yet
@ef4
Contributor

ef4 commented Apr 8, 2015

This implements template-driven component integration tests. See previous discussion in #25.

TestModuleForComponent now offers both a unit test mode (which is unchanged from the present behavior) and an integration test mode (which subsumes previous ModuleForIntegration).

The default is integration test mode, because I am 100% convinced that is the way most people should be testing most components most of the time. We switch to unit test mode if you provide an explicit needs argument, or say unit: true, or say integration:false.

In integration mode, tests are template-driven, giving you full and intuitive access to all features of components, including passing them block templates, yielding block params, and action handling.

People with existing component unit tests that already have a needs parameter (which I expect is most of them) will not be affected by this change. But people with component unit tests and no needs parameter will get failing tests, until they add unit: true to their module. This is a very low-cost upgrade pain, so I think it's worth it for achieving a simpler API that nudges people toward the right choice by default.

Edited to add: after feedback, I changed this to not be a breaking change. Unit test mode is still the default, but we will hit you with a deprecation warning unless you add an explicit needs or unit:true, which will protect you from a future change that makes integration mode the default.

I have tested this change with both ember-mocha and ember-qunit, and no changes are actually required in either of them to use this functionality. We will want to update their ember-cli blueprint generators however, so that they can generate both unit and integration tests for components, and so they place them under an appropriate path (like tests/integration instead of tests/unit).

@@ -133,6 +133,7 @@ export default Klass.extend({
setContext({
container: this.container,
registry: this.registry,

This comment has been minimized.

@ef4

ef4 Apr 8, 2015

Contributor

Exposing the registry to the test context lets people move to the non-deprecated registration APIs. This starts giving us a path away from the hacks that are currently used to avoid deprecations.

@ef4

ef4 Apr 8, 2015

Contributor

Exposing the registry to the test context lets people move to the non-deprecated registration APIs. This starts giving us a path away from the hacks that are currently used to avoid deprecations.

@rwjblue

This comment has been minimized.

Show comment
Hide comment
@rwjblue

rwjblue Apr 8, 2015

Member

I have a few tiny comments, but the approach looks good to me.

Member

rwjblue commented Apr 8, 2015

I have a few tiny comments, but the approach looks good to me.

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 Apr 8, 2015

Contributor

Based on feedback from @rwjblue I have made this a less jarring upgrade. For now we still default to unit test mode, but will fire a deprecation warning if you are in danger of getting the new behavior when we finally do switch the default.

Contributor

ef4 commented Apr 8, 2015

Based on feedback from @rwjblue I have made this a less jarring upgrade. For now we still default to unit test mode, but will fire a deprecation warning if you are in danger of getting the new behavior when we finally do switch the default.

ef4 added a commit to ember-animation/liquid-fire that referenced this pull request Apr 8, 2015

@rwjblue

This comment has been minimized.

Show comment
Hide comment
@rwjblue

rwjblue Apr 8, 2015

Member

LGTM.

@dgeb - Any objections?

Member

rwjblue commented Apr 8, 2015

LGTM.

@dgeb - Any objections?

@dgeb

This comment has been minimized.

Show comment
Hide comment
@dgeb

dgeb Apr 9, 2015

Member

I was never crazy about the name TestModuleForIntegration, but it clearly provide a free-form template sandbox into which you can render any elements. You can integration test a single component or multiple components acting together. You can even test multiple configurations of the same component. None of that really goes away with these changes, but the naming gives the appearance that template-driven integration tests are tied to a single component.

This is not just an issue with component integration tests. The integration flag on our modules allows for sharing of an application instance's registry/container, but still leads the developer to focusing tests on a single thing: model, component or otherwise.

I think I'd rather see us introduce integration test modules that provide the semantics to test actual integrations. Perhaps IntegrationTestModule, ModelIntegrationTestModule, and ComponentIntegrationTestModule? The basic IntegrationTestModule could provide access to just an ApplicationInstance, the ModelIntegrationTestModule could also automatically provide a store, and the ComponentIntegrationTestModule could provide a free-form template. None of them would be tied to a single subject however.

@ef4 - I really appreciate the hard work you've done to enable these integration capabilities. I would like to make sure we expose these capabilities in the most useful light to users via the higher level libs (ember-qunit and ember-mocha) and through the blueprints in ember-cli.

Member

dgeb commented Apr 9, 2015

I was never crazy about the name TestModuleForIntegration, but it clearly provide a free-form template sandbox into which you can render any elements. You can integration test a single component or multiple components acting together. You can even test multiple configurations of the same component. None of that really goes away with these changes, but the naming gives the appearance that template-driven integration tests are tied to a single component.

This is not just an issue with component integration tests. The integration flag on our modules allows for sharing of an application instance's registry/container, but still leads the developer to focusing tests on a single thing: model, component or otherwise.

I think I'd rather see us introduce integration test modules that provide the semantics to test actual integrations. Perhaps IntegrationTestModule, ModelIntegrationTestModule, and ComponentIntegrationTestModule? The basic IntegrationTestModule could provide access to just an ApplicationInstance, the ModelIntegrationTestModule could also automatically provide a store, and the ComponentIntegrationTestModule could provide a free-form template. None of them would be tied to a single subject however.

@ef4 - I really appreciate the hard work you've done to enable these integration capabilities. I would like to make sure we expose these capabilities in the most useful light to users via the higher level libs (ember-qunit and ember-mocha) and through the blueprints in ember-cli.

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 Apr 9, 2015

Contributor

I'm not 100% clear on what your position is on this.

I realize "unit" and "integration" mean different things in different
places, and I don't necessarily care which interpretation we pick as long
as we're consistent.

But it is clear that testing against public API is better than testing
against private API. And the main public API components expose is via
templates.
And it is also clear that component tests -- even the existing ones -- are
actually testing integration between your component and many other moving
parts: htmlbars, ember, any built-in helpers or keywords, the long list of
things inside isolatedContainer. That is the kind of integration I'm
talking about.

If we continue to generate component "unit" tests by default when people
run ember g component, I think we are actively leading them down the
wrong path, by encouraging them to test against private API and by not
giving them easy access to all the template features like blocks and block
params.

Contributor

ef4 commented Apr 9, 2015

I'm not 100% clear on what your position is on this.

I realize "unit" and "integration" mean different things in different
places, and I don't necessarily care which interpretation we pick as long
as we're consistent.

But it is clear that testing against public API is better than testing
against private API. And the main public API components expose is via
templates.
And it is also clear that component tests -- even the existing ones -- are
actually testing integration between your component and many other moving
parts: htmlbars, ember, any built-in helpers or keywords, the long list of
things inside isolatedContainer. That is the kind of integration I'm
talking about.

If we continue to generate component "unit" tests by default when people
run ember g component, I think we are actively leading them down the
wrong path, by encouraging them to test against private API and by not
giving them easy access to all the template features like blocks and block
params.

@stefanpenner

This comment has been minimized.

Show comment
Hide comment
@stefanpenner

stefanpenner Apr 9, 2015

Member

in general i like to define various testing levels the following way:

  • unit: algorithmic complexity + no collaborators (public collaborators)
  • integration: low-level interaction between collaborators (mocks for foreign collaborators)
  • acceptance: black-box signals sent as the agent without internals leaking forward (exceptions include faking/mocking edge interfaces – adapters)

I believe components in ember, describe the JS/template that controls a chunk of template hierarchy. As such I do not believe today one can purely unit test this. It would have to be some adapted unit or more truthfully an integration mode of sorts.

We should explore a future where this is possible, I believe we can improve it, but pragmatically speaking the path of least resistance today (and short term) is likely more permissive component testing helpers.

This problem is shared by WebComponents and demonstrates that they aren't as isolated as people think. Their nested components are hidden, style and structure don't leak, but the implementation isn't actually encapsulated as at this phase, how to load/initialize and setup their descendants leaks forward.

a concrete example we can build a discussion around:

<my-cool-component>
  <!-- public and external -->
</my-cool-component> 
<!-- my-cool-component.html -->
<my-internal-component />
<input/>
<!-- my-internal-component.html -->
<my-other-internal-component />

When testing: my-cool-component we must introduce both my-internal-component and my-other-internal-component to the environment, or testing becomes impossible.

We do have the flexibility today to provide a mock my-internal-component that the my-component tester controls, but I believe the most common need we have is testing non-mock collaborators strung up in their natural template hierarchy .

Member

stefanpenner commented Apr 9, 2015

in general i like to define various testing levels the following way:

  • unit: algorithmic complexity + no collaborators (public collaborators)
  • integration: low-level interaction between collaborators (mocks for foreign collaborators)
  • acceptance: black-box signals sent as the agent without internals leaking forward (exceptions include faking/mocking edge interfaces – adapters)

I believe components in ember, describe the JS/template that controls a chunk of template hierarchy. As such I do not believe today one can purely unit test this. It would have to be some adapted unit or more truthfully an integration mode of sorts.

We should explore a future where this is possible, I believe we can improve it, but pragmatically speaking the path of least resistance today (and short term) is likely more permissive component testing helpers.

This problem is shared by WebComponents and demonstrates that they aren't as isolated as people think. Their nested components are hidden, style and structure don't leak, but the implementation isn't actually encapsulated as at this phase, how to load/initialize and setup their descendants leaks forward.

a concrete example we can build a discussion around:

<my-cool-component>
  <!-- public and external -->
</my-cool-component> 
<!-- my-cool-component.html -->
<my-internal-component />
<input/>
<!-- my-internal-component.html -->
<my-other-internal-component />

When testing: my-cool-component we must introduce both my-internal-component and my-other-internal-component to the environment, or testing becomes impossible.

We do have the flexibility today to provide a mock my-internal-component that the my-component tester controls, but I believe the most common need we have is testing non-mock collaborators strung up in their natural template hierarchy .

@dgeb

This comment has been minimized.

Show comment
Hide comment
@dgeb

dgeb Apr 9, 2015

Member

@ef4 Sorry I was not clear. I am not disagreeing, but seeking consensus on what we consider integration tests to be in ember.

From a pragmatic standpoint, I agree that the kind of integration test in this PR should be the default for testing a component. As @stefanpenner points out, there is no way to purely unit test components in ember today.

My point above is that there is room for a more general purpose layer of "integration" tests here. Integration tests that are focused on testing the interaction of multiple components together, multiple models together, or perhaps other aspects of an application instance. TestModuleForIntegration provides a template-driven sandbox for this kind of general integration test, albeit with an awkward name.

So really I'd like to come to an agreement on semantics to allow for space for general integration tests. I raised this here because this PR removes TestModuleForIntegration.

I agree with @stefanpenner's take on testing levels. So how do we translate those levels to these unit-ish integration tests of components? In which directory are these component test blueprints going to be placed? They seem to be the duck-billed platypus of testing.

Member

dgeb commented Apr 9, 2015

@ef4 Sorry I was not clear. I am not disagreeing, but seeking consensus on what we consider integration tests to be in ember.

From a pragmatic standpoint, I agree that the kind of integration test in this PR should be the default for testing a component. As @stefanpenner points out, there is no way to purely unit test components in ember today.

My point above is that there is room for a more general purpose layer of "integration" tests here. Integration tests that are focused on testing the interaction of multiple components together, multiple models together, or perhaps other aspects of an application instance. TestModuleForIntegration provides a template-driven sandbox for this kind of general integration test, albeit with an awkward name.

So really I'd like to come to an agreement on semantics to allow for space for general integration tests. I raised this here because this PR removes TestModuleForIntegration.

I agree with @stefanpenner's take on testing levels. So how do we translate those levels to these unit-ish integration tests of components? In which directory are these component test blueprints going to be placed? They seem to be the duck-billed platypus of testing.

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 Apr 9, 2015

Contributor

Ah, I understand better now.

I guess we could still offer some more generic kind of integration test,
which would have pretty much the same implementation but a different name.

I'm hesitant though. More kinds of tests pushes more choice burden onto
users. Especially since we also still have acceptance tests.

In an Ember 2.0 world, everything is a component, so at every layer of your
app there is some component for which you can say moduleForComponent. I
think this necessarily covers all the multi-component interactions that
matter to your app, since they must occur within some enclosing component,
and that gives you a good place to put the integration test.
On Apr 9, 2015 5:19 PM, "Dan Gebhardt" notifications@github.com wrote:

@ef4 https://github.com/ef4 Sorry I was not clear. I am not
disagreeing, but seeking consensus on what we consider integration tests to
be in ember.

From a pragmatic standpoint, I agree that the kind of integration test in
this PR should be the default for testing a component. As @stefanpenner
https://github.com/stefanpenner points out, there is no way to purely unit
test
components in ember today.

My point above is that there is room for a more general purpose layer of
"integration" tests here. Integration tests that are focused on testing the
interaction of multiple components together, multiple models together, or
perhaps other aspects of an application instance. TestModuleForIntegration
provides a template-driven sandbox for this kind of general integration
test, albeit with an awkward name.

So really I'd like to come to an agreement on semantics to allow for space
for general integration tests. I raised this here because this PR removes
TestModuleForIntegration.

I agree with @stefanpenner https://github.com/stefanpenner's take on
testing levels. So how do we translate those levels to these unit-ish
integration tests of components? In which directory are these component
test blueprints going to be placed? They seem to be the duck-billed
platypus of testing.


Reply to this email directly or view it on GitHub
#38 (comment)
.

Contributor

ef4 commented Apr 9, 2015

Ah, I understand better now.

I guess we could still offer some more generic kind of integration test,
which would have pretty much the same implementation but a different name.

I'm hesitant though. More kinds of tests pushes more choice burden onto
users. Especially since we also still have acceptance tests.

In an Ember 2.0 world, everything is a component, so at every layer of your
app there is some component for which you can say moduleForComponent. I
think this necessarily covers all the multi-component interactions that
matter to your app, since they must occur within some enclosing component,
and that gives you a good place to put the integration test.
On Apr 9, 2015 5:19 PM, "Dan Gebhardt" notifications@github.com wrote:

@ef4 https://github.com/ef4 Sorry I was not clear. I am not
disagreeing, but seeking consensus on what we consider integration tests to
be in ember.

From a pragmatic standpoint, I agree that the kind of integration test in
this PR should be the default for testing a component. As @stefanpenner
https://github.com/stefanpenner points out, there is no way to purely unit
test
components in ember today.

My point above is that there is room for a more general purpose layer of
"integration" tests here. Integration tests that are focused on testing the
interaction of multiple components together, multiple models together, or
perhaps other aspects of an application instance. TestModuleForIntegration
provides a template-driven sandbox for this kind of general integration
test, albeit with an awkward name.

So really I'd like to come to an agreement on semantics to allow for space
for general integration tests. I raised this here because this PR removes
TestModuleForIntegration.

I agree with @stefanpenner https://github.com/stefanpenner's take on
testing levels. So how do we translate those levels to these unit-ish
integration tests of components? In which directory are these component
test blueprints going to be placed? They seem to be the duck-billed
platypus of testing.


Reply to this email directly or view it on GitHub
#38 (comment)
.

@dgeb dgeb referenced this pull request Apr 13, 2015

Merged

Registry / Container reform #46

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 Apr 15, 2015

Contributor

I would like to help find consensus so we can land this feature.

It sounds like everyone supports the idea of TestModuleForComponent having an integration mode. The remaining discussion is around whether we should also have another kind of test that is more sandbox-like, without implying it's tied to a single component.

The two issues are separable. They got tied together because I eliminated TestModuleForIntegration -- which never managed to ship to users because we could never find a good UI for it. But to clarify: this PR does not foreclose the possibility of also offering more sandbox-like tests. In fact, I don't think such tests would even need to be implemented separately here in ember-test-helpers. TestModuleForComponent is a sufficiently general primitive that ember-qunit can use it to expose moduleForSnippet or ember-mocha could use it to expose describeScenario, etc. We are still free to design that API.

So I propose merging the uncontroversial feature (integration-mode for components), instead of waiting until we can design this other possible thing (sandbox tests). I suspect that design will not be trivial, which is why TestModuleForIntegration has been languishing.

Contributor

ef4 commented Apr 15, 2015

I would like to help find consensus so we can land this feature.

It sounds like everyone supports the idea of TestModuleForComponent having an integration mode. The remaining discussion is around whether we should also have another kind of test that is more sandbox-like, without implying it's tied to a single component.

The two issues are separable. They got tied together because I eliminated TestModuleForIntegration -- which never managed to ship to users because we could never find a good UI for it. But to clarify: this PR does not foreclose the possibility of also offering more sandbox-like tests. In fact, I don't think such tests would even need to be implemented separately here in ember-test-helpers. TestModuleForComponent is a sufficiently general primitive that ember-qunit can use it to expose moduleForSnippet or ember-mocha could use it to expose describeScenario, etc. We are still free to design that API.

So I propose merging the uncontroversial feature (integration-mode for components), instead of waiting until we can design this other possible thing (sandbox tests). I suspect that design will not be trivial, which is why TestModuleForIntegration has been languishing.

@dgeb

This comment has been minimized.

Show comment
Hide comment
@dgeb

dgeb Apr 15, 2015

Member

@ef4 sorry for stalling on this. I am fine with merging once we resolve the isolatedContainer issue.

Member

dgeb commented Apr 15, 2015

@ef4 sorry for stalling on this. I am fine with merging once we resolve the isolatedContainer issue.

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 Apr 15, 2015

Contributor

No problem -- this is definitely an area where having a clear story is important.

Contributor

ef4 commented Apr 15, 2015

No problem -- this is definitely an area where having a clear story is important.

@danfinlay

This comment has been minimized.

Show comment
Hide comment
@danfinlay

danfinlay May 1, 2015

Does this include scaffolding the needs relationships for model tests? I was referred here because I suggested that feature over at Ember-CLI.

danfinlay commented May 1, 2015

Does this include scaffolding the needs relationships for model tests? I was referred here because I suggested that feature over at Ember-CLI.

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 May 2, 2015

Contributor

Finally got around to making the last requested change, so that isolatedContainer maintains the same public API. This should be ready to go.

Contributor

ef4 commented May 2, 2015

Finally got around to making the last requested change, so that isolatedContainer maintains the same public API. This should be ready to go.

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 May 2, 2015

Contributor

@FlySwatter I don't think you actually need this PR to get the behavior you want in a model test. You should already be able to pass integration: true to get a model test that doesn't need an explicit needs list, which is an appropriate way to integration test a model and its dependencies.

We do still need some clear documentation around that, and possibly some new blueprint generators.

Contributor

ef4 commented May 2, 2015

@FlySwatter I don't think you actually need this PR to get the behavior you want in a model test. You should already be able to pass integration: true to get a model test that doesn't need an explicit needs list, which is an appropriate way to integration test a model and its dependencies.

We do still need some clear documentation around that, and possibly some new blueprint generators.

@rwjblue

This comment has been minimized.

Show comment
Hide comment
@rwjblue

rwjblue May 2, 2015

Member

There is still a bit of work to do so that we do not have to include the template precompiler (on the ember-cli side), but once this lands we can iterate on that issue separately.

I am 👍 on landing this PR.

Member

rwjblue commented May 2, 2015

There is still a bit of work to do so that we do not have to include the template precompiler (on the ember-cli side), but once this lands we can iterate on that issue separately.

I am 👍 on landing this PR.

@danfinlay

This comment has been minimized.

Show comment
Hide comment
@danfinlay

danfinlay May 2, 2015

Oh wow! That's a great feature, I never saw it documented (and lived on the testing guide for a while a bit back).

I would think that would be a sensible default for the model generators, personally!

  • Dan

On May 1, 2015, at 6:51 PM, Edward Faulkner notifications@github.com wrote:

@FlySwatter I don't think you actually need this PR to get the behavior you want in a model test. You should already be able to pass integration: true to get a model test that doesn't need an explicit needs list, which is an appropriate way to integration test a model and its dependencies.

We do still need some clear documentation around that, and possibly some new blueprint generators.


Reply to this email directly or view it on GitHub.

danfinlay commented May 2, 2015

Oh wow! That's a great feature, I never saw it documented (and lived on the testing guide for a while a bit back).

I would think that would be a sensible default for the model generators, personally!

  • Dan

On May 1, 2015, at 6:51 PM, Edward Faulkner notifications@github.com wrote:

@FlySwatter I don't think you actually need this PR to get the behavior you want in a model test. You should already be able to pass integration: true to get a model test that doesn't need an explicit needs list, which is an appropriate way to integration test a model and its dependencies.

We do still need some clear documentation around that, and possibly some new blueprint generators.


Reply to this email directly or view it on GitHub.

@jamesarosen

This comment has been minimized.

Show comment
Hide comment
@jamesarosen

jamesarosen May 2, 2015

Here's a thing that I would have expected to work, but does not:

// my-component/component.js:
export default Ember.Component.extend({
  value: 0,

  incrementOnClick: Ember.on('click', function() {
    this.incrementProperty('value');
  });
});

{{!-- my-component/template.hbs --}}
<span class='target'>Click to increment!</span>
<span class='value'>{{value}}</span>

// my-component-test.js:
moduleForComponent('my-component', {
  beforeEach: function() {
    this.render('{{my-component}}');
  }
});

test('clicking increments', function(assert) {
  this.$('.target').click(); // or wrap in Ember.run
  assert.equal(this.$('.value').text(), '1');
});

That is, {{action}} works, but Ember.on('click') doesn't. That may be intentional or you may not particularly care.

jamesarosen commented May 2, 2015

Here's a thing that I would have expected to work, but does not:

// my-component/component.js:
export default Ember.Component.extend({
  value: 0,

  incrementOnClick: Ember.on('click', function() {
    this.incrementProperty('value');
  });
});

{{!-- my-component/template.hbs --}}
<span class='target'>Click to increment!</span>
<span class='value'>{{value}}</span>

// my-component-test.js:
moduleForComponent('my-component', {
  beforeEach: function() {
    this.render('{{my-component}}');
  }
});

test('clicking increments', function(assert) {
  this.$('.target').click(); // or wrap in Ember.run
  assert.equal(this.$('.value').text(), '1');
});

That is, {{action}} works, but Ember.on('click') doesn't. That may be intentional or you may not particularly care.

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 May 2, 2015

Contributor

I suspect the can helper is getting manually registered in an application
initializer. Initializers don't run for these tests, and we don't really
want them to, because booting a whole app is more expensive.

That's one reason it's easier to stick with the naming conventions.
Unfortunately helpers without a dash are not auto discovered.
On May 2, 2015 5:31 PM, "James Alexander Rosen" notifications@github.com
wrote:

When using ember-can https://github.com/minutebase/ember-can's {{if
(can "view thing")}} helper in a component's template along with this
branch (and integration: true), I get

Uncaught Error: Assertion Failed: A helper named 'can' could not be found

Yet if I pause right before the this.render call in my component test,
then this.registry.lookup('helper:can') is the right helper!

All the other lookups seem to be working. It's possible that the problem
lies in how that library defines the helper. Or it could be the way this
isolated resolver works.


Reply to this email directly or view it on GitHub
#38 (comment)
.

Contributor

ef4 commented May 2, 2015

I suspect the can helper is getting manually registered in an application
initializer. Initializers don't run for these tests, and we don't really
want them to, because booting a whole app is more expensive.

That's one reason it's easier to stick with the naming conventions.
Unfortunately helpers without a dash are not auto discovered.
On May 2, 2015 5:31 PM, "James Alexander Rosen" notifications@github.com
wrote:

When using ember-can https://github.com/minutebase/ember-can's {{if
(can "view thing")}} helper in a component's template along with this
branch (and integration: true), I get

Uncaught Error: Assertion Failed: A helper named 'can' could not be found

Yet if I pause right before the this.render call in my component test,
then this.registry.lookup('helper:can') is the right helper!

All the other lookups seem to be working. It's possible that the problem
lies in how that library defines the helper. Or it could be the way this
isolated resolver works.


Reply to this email directly or view it on GitHub
#38 (comment)
.

@jamesarosen

This comment has been minimized.

Show comment
Hide comment
@jamesarosen

jamesarosen May 2, 2015

@ef4 great catch on the can helper. I may register some things myself in the tests. It's possible that many of my component tests should really be integration tests.

jamesarosen commented May 2, 2015

@ef4 great catch on the can helper. I may register some things myself in the tests. It's possible that many of my component tests should really be integration tests.

@stefanpenner

This comment has been minimized.

Show comment
Hide comment
@stefanpenner

stefanpenner May 3, 2015

Member

babel will likely start making it so that we can pre-compile easily via string templates: see: https://gist.github.com/stefanpenner/38a36298d04b29bbce5f

This would allow us to do this, without having to include the pre-compiler.

Member

stefanpenner commented May 3, 2015

babel will likely start making it so that we can pre-compile easily via string templates: see: https://gist.github.com/stefanpenner/38a36298d04b29bbce5f

This would allow us to do this, without having to include the pre-compiler.

dgeb added a commit that referenced this pull request May 3, 2015

@dgeb dgeb merged commit 40ad0bf into emberjs:master May 3, 2015

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
@dgeb

This comment has been minimized.

Show comment
Hide comment
@dgeb

dgeb May 3, 2015

Member

Thanks for all your work (and patience) @ef4! :shipit:

Member

dgeb commented May 3, 2015

Thanks for all your work (and patience) @ef4! :shipit:

@rwjblue

This comment has been minimized.

Show comment
Hide comment
@rwjblue

rwjblue May 3, 2015

Member

@stefanpenner - That gist looks nice, but still seems that it would use runtime compilation (not build time). Unless you are saying there are additional hooks added in babel.

(we could of course write a babel transform that does this for us though)

Member

rwjblue commented May 3, 2015

@stefanpenner - That gist looks nice, but still seems that it would use runtime compilation (not build time). Unless you are saying there are additional hooks added in babel.

(we could of course write a babel transform that does this for us though)

@stefanpenner

This comment has been minimized.

Show comment
Hide comment
@stefanpenner

stefanpenner May 3, 2015

Member

@stefanpenner - That gist looks nice, but still seems that it would use runtime compilation (not build time). Unless you are saying there are additional hooks added in babel.

it would detect the string template handler, and pre-compile at build time. This allows valid language syntax to be used, but as an optimization (for both perf and dep size) it would be done at build time rather then runtime.

Member

stefanpenner commented May 3, 2015

@stefanpenner - That gist looks nice, but still seems that it would use runtime compilation (not build time). Unless you are saying there are additional hooks added in babel.

it would detect the string template handler, and pre-compile at build time. This allows valid language syntax to be used, but as an optimization (for both perf and dep size) it would be done at build time rather then runtime.

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 May 3, 2015

Contributor

@jamesarosen there does seem to be something wrong with the action handling. I would have expected your example to work. I will investigate further.

Contributor

ef4 commented May 3, 2015

@jamesarosen there does seem to be something wrong with the action handling. I would have expected your example to work. I will investigate further.

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 May 3, 2015

Contributor

I figured out the event handling problem. It's caused by doing render during the setup instead of during the test. If you move the render call into the test itself the problem goes away.

ember-qunit does a fairly wacky thing at the start of every test that breaks event handling for any view created before that point. So you can't render in setup/beforeEach.

Contributor

ef4 commented May 3, 2015

I figured out the event handling problem. It's caused by doing render during the setup instead of during the test. If you move the render call into the test itself the problem goes away.

ember-qunit does a fairly wacky thing at the start of every test that breaks event handling for any view created before that point. So you can't render in setup/beforeEach.

@ef4

This comment has been minimized.

Show comment
Hide comment
@ef4

ef4 May 3, 2015

Contributor

There is an existing ember-qunit issue about this behavior. emberjs/ember-qunit#86

Contributor

ef4 commented May 3, 2015

There is an existing ember-qunit issue about this behavior. emberjs/ember-qunit#86

@technomage

This comment has been minimized.

Show comment
Hide comment
@technomage

technomage May 4, 2015

I am all for this feature. I have borrowed @ef4 version from his liquid-fire repo and it is significantly better than the current unit tests for components.

Not having initializers run in general, I understand, but there should be a clean way to spin up ember data in this environment. When components replace controllers, there needs to be a way to test the component/data interface without reimplementing Ember Data for each test.

technomage commented May 4, 2015

I am all for this feature. I have borrowed @ef4 version from his liquid-fire repo and it is significantly better than the current unit tests for components.

Not having initializers run in general, I understand, but there should be a clean way to spin up ember data in this environment. When components replace controllers, there needs to be a way to test the component/data interface without reimplementing Ember Data for each test.

@mgenev

This comment has been minimized.

Show comment
Hide comment
@mgenev

mgenev May 4, 2015

@technomage

Not having initializers run in general, I understand, but there should be a clean way to spin up ember data in this environment. When components replace controllers, there needs to be a way to test the component/data interface without reimplementing Ember Data for each test.

I agree so much!

mgenev commented May 4, 2015

@technomage

Not having initializers run in general, I understand, but there should be a clean way to spin up ember data in this environment. When components replace controllers, there needs to be a way to test the component/data interface without reimplementing Ember Data for each test.

I agree so much!

@pangratz

This comment has been minimized.

Show comment
Hide comment
@pangratz

pangratz May 5, 2015

Member

I played around with babel and created a babel-htmlbars-precompile plugin, which replaces a tagged template string with the precompiled HTMLBars template:

// babel-htmlbars-compiler/index.js
var compiler = require('./ember-template-compiler');

module.exports = function(babel) {
  var t = babel.types;

  return new babel.Transformer('babel-htmlbars-precompile', {
    TaggedTemplateExpression: function(node) {
      if (t.isIdentifier(node.tag, { name: "hbs" })) {
        var quasis = node.quasi.quasis;
        var template = quasis.map(function(quasi) {
          return quasi.value.cooked;
        }).join("");

        return "Ember.HTMLBars.template(" + compiler.precompile(template) + ")";
      }
    }
  });
};

which in turn can be used within Ember-CLI:

// Brocfile.js
var EmberApp = require('ember-cli/lib/broccoli/ember-app');

var app = new EmberApp({
  babel: {
    plugins: ['babel-htmlbars-precompile']
  }
});

module.exports = app.toTree();

and this allows to write for example a component test like this:

import { moduleForComponent, test } from 'ember-qunit';

moduleForComponent('my-component');

test('it renders', function(assert) {
  var component = this.subject({
    hello: "world",
    layout: hbs`huhu {{hello}}`
  });

  // Renders the component to the page
  assert.equal(this.$().text().trim(), "huhu world");
});

@stefanpenner is this something you had in mind with your gist https://gist.github.com/stefanpenner/38a36298d04b29bbce5f?

Member

pangratz commented May 5, 2015

I played around with babel and created a babel-htmlbars-precompile plugin, which replaces a tagged template string with the precompiled HTMLBars template:

// babel-htmlbars-compiler/index.js
var compiler = require('./ember-template-compiler');

module.exports = function(babel) {
  var t = babel.types;

  return new babel.Transformer('babel-htmlbars-precompile', {
    TaggedTemplateExpression: function(node) {
      if (t.isIdentifier(node.tag, { name: "hbs" })) {
        var quasis = node.quasi.quasis;
        var template = quasis.map(function(quasi) {
          return quasi.value.cooked;
        }).join("");

        return "Ember.HTMLBars.template(" + compiler.precompile(template) + ")";
      }
    }
  });
};

which in turn can be used within Ember-CLI:

// Brocfile.js
var EmberApp = require('ember-cli/lib/broccoli/ember-app');

var app = new EmberApp({
  babel: {
    plugins: ['babel-htmlbars-precompile']
  }
});

module.exports = app.toTree();

and this allows to write for example a component test like this:

import { moduleForComponent, test } from 'ember-qunit';

moduleForComponent('my-component');

test('it renders', function(assert) {
  var component = this.subject({
    hello: "world",
    layout: hbs`huhu {{hello}}`
  });

  // Renders the component to the page
  assert.equal(this.$().text().trim(), "huhu world");
});

@stefanpenner is this something you had in mind with your gist https://gist.github.com/stefanpenner/38a36298d04b29bbce5f?

@rwjblue

This comment has been minimized.

Show comment
Hide comment
@rwjblue

rwjblue May 5, 2015

Member

@pangratz - Perfect!

Member

rwjblue commented May 5, 2015

@pangratz - Perfect!

@pangratz

This comment has been minimized.

Show comment
Hide comment
@pangratz

pangratz May 5, 2015

Member

Alright, I've published the inline precompile stuff as EmberCLI addon: ember-cli-htmlbars-inline-precompile.

Member

pangratz commented May 5, 2015

Alright, I've published the inline precompile stuff as EmberCLI addon: ember-cli-htmlbars-inline-precompile.

@ballPointPenguin

This comment has been minimized.

Show comment
Hide comment
@ballPointPenguin

ballPointPenguin May 22, 2015

Reading threads like this reminds me why I love the Ember community. 😍

ballPointPenguin commented May 22, 2015

Reading threads like this reminds me why I love the Ember community. 😍

seanpdoyle added a commit to seanpdoyle/ember-test-helpers that referenced this pull request Jun 8, 2015

Provide better context for `subject()` deprecation
The outcome of [#38][38] is undocumented. 

Raising a deprecation warning for calls to `this.subject` is a good start, 
but could be improved greatly.

[38]: emberjs#38

@rwjblue rwjblue referenced this pull request Jun 12, 2015

Closed

Testing component blocks #158

@Emerson

This comment has been minimized.

Show comment
Hide comment
@Emerson

Emerson Jul 13, 2015

Would love to see a few robust examples of this in action. I'm trying to do some pretty basic stuff and I'm not sure the best way of approaching the problem. As a simple example:

test('submit-button can be disabled', function(assert) {
  this.render(hbs `{{submit-button disabled=true}}`);

  assert.ok(this.$('button:disabled').length, 'It rendered a disabled submit button');
 // Great! This works.

  // Now I want to change the disabled property to false and assert that the button 
  // is no longer disabled. What's the best way of doing that?
});

I'm sure there is an easy answer to this question (just call render again?), but some more real world examples would be useful for those of us trying to migrate our tests over to this style.

Also interested in how we pass more complicated data into our components (arrays, objects, etc...).

Love the PR - thanks!

Update: rerendered the component and it works, but is that a new instance of the component? Or rerender of the original...

Emerson commented Jul 13, 2015

Would love to see a few robust examples of this in action. I'm trying to do some pretty basic stuff and I'm not sure the best way of approaching the problem. As a simple example:

test('submit-button can be disabled', function(assert) {
  this.render(hbs `{{submit-button disabled=true}}`);

  assert.ok(this.$('button:disabled').length, 'It rendered a disabled submit button');
 // Great! This works.

  // Now I want to change the disabled property to false and assert that the button 
  // is no longer disabled. What's the best way of doing that?
});

I'm sure there is an easy answer to this question (just call render again?), but some more real world examples would be useful for those of us trying to migrate our tests over to this style.

Also interested in how we pass more complicated data into our components (arrays, objects, etc...).

Love the PR - thanks!

Update: rerendered the component and it works, but is that a new instance of the component? Or rerender of the original...

@rwjblue

This comment has been minimized.

Show comment
Hide comment
@rwjblue

rwjblue Jul 13, 2015

Member

This should do the trick:

test('submit-button can be disabled', function(assert) {
  this.set('currentDisabledValue', true);

  this.render(hbs `{{submit-button disabled=currentDisabledValue}}`);

  assert.ok(this.$('button:disabled').length, 'It rendered a disabled submit button');

  this.set('currentDisabledValue', false);

  assert.equal(this.$('button:disabled').length, 0, 'It switched to disabled');
});

Links:

Member

rwjblue commented Jul 13, 2015

This should do the trick:

test('submit-button can be disabled', function(assert) {
  this.set('currentDisabledValue', true);

  this.render(hbs `{{submit-button disabled=currentDisabledValue}}`);

  assert.ok(this.$('button:disabled').length, 'It rendered a disabled submit button');

  this.set('currentDisabledValue', false);

  assert.equal(this.$('button:disabled').length, 0, 'It switched to disabled');
});

Links:

@Emerson

This comment has been minimized.

Show comment
Hide comment
@Emerson

Emerson Jul 13, 2015

Ahh - makes good sense! Thanks @rwjblue

Emerson commented Jul 13, 2015

Ahh - makes good sense! Thanks @rwjblue

seanpdoyle added a commit to seanpdoyle/ember-test-helpers that referenced this pull request Dec 9, 2015

Provide better context for `subject()` deprecation
The outcome of [#38][38] is undocumented. 

Raising a deprecation warning for calls to `this.subject` is a good start, 
but could be improved greatly.

[38]: emberjs#38

seanpdoyle added a commit to seanpdoyle/ember-test-helpers that referenced this pull request Dec 9, 2015

Provide better context for `subject()` deprecation
The outcome of [#38][38] is undocumented.

Raising a deprecation warning for calls to `this.subject` is a good start,
but could be improved greatly.

[38]: emberjs#38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment