How do I test a Component with Block-Params?


I have a component that yields a block-param. It’s meant to be used like

{{#my-component as |x|}}
  Use {{x}} somehow.

Can’t Use Test Helpers

I can’t use ember-test-helper’s test-module-for-component (or ember-qunit’s moduleForComponent) because I need the as |x| in the invocation of my-component.

(I’ve opened as a feature-request for test-module-for-component to allow specifying block params, but I don’t know exactly how it would do that.

My Unit Test

In the mean time, I’ve tried writing a test. Here’s what I have so far:

import Ember from "ember";
import { moduleFor, test } from "ember-qunit";
import startApp from '../../../../helpers/start-app';

var App;

const TEMPLATE = `
{{#my-component as |x|}}

moduleFor('my-component', {
  integration: true,

  beforeEach: function(assert) {
    App = startApp();
    const View = App.__container__.lookupFactory('view:default');

    this.view = View.create({
      tagName: 'section',
      template: Ember.Handlebars.compile(TEMPLATE)
    });, 'append');

  afterEach: function() {, 'destroy');, 'destroy');
    App = null;

test('yields x value', function(assert) {
  assert.equal(this.view.$('span').text(), 'x');

When I run that, I get

TypeError: Cannot read property 'element' of null
    at Object.merge.default.$ (http://localhost:7357/assets/vendor.js:51942:42)
    at CoreView.default.extend.$ (http://localhost:7357/assets/vendor.js:53179:32)
    at Object.<anonymous> (http://localhost:7357/assets/my-app.js:10105:28)
    at Object.wrapper (http://localhost:7357/assets/test-support.js:1785:29)
    at (http://localhost:7357/assets/test-support.js:3443:28)
    at http://localhost:7357/assets/test-support.js:3572:11
    at process (http://localhost:7357/assets/test-support.js:3131:24)
    at begin (http://localhost:7357/assets/test-support.js:3176:2)
    at http://localhost:7357/assets/test-support.js:3192:4

That error is happening in hasElement.$.

Things I Tried

  • Ember.View.create instead of looking up view:default on the app, but then the view could find my-component
  • view.render() instead of view.append(), but that instead gives the error “Cannot read property ‘innerContextualElement’ of undefined”
  • not specifying tagName, but that instead gives the error “You cannot access this.$() on a component with tagName: '' specified.”

Didn’t dig in deeper (but hopefully will, as I’m very interested in the “why”), but extending an Ember.View with setting it’s container property to the App’s __container__, and then creating it seems to solve the problem:

  beforeEach: function(assert) {
    App = startApp();

    this.view = Ember.View.extend({
      container: App.__container__
      tagName: 'section',
      template: Ember.Handlebars.compile(TEMPLATE)
    });, 'append');

So basically the first thing you tried almost got you there :slight_smile:
But like I said, I have no idea why view:default didn’t work, would love to find out…

It looks like there’s an official solution for this coming: