Skip to content
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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions lib/ember-test-helpers.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import Ember from 'ember';
import isolatedContainer from 'ember-test-helpers/isolated-container';
import { isolatedContainer } from 'ember-test-helpers/isolated-container';
import TestModule from 'ember-test-helpers/test-module';
import TestModuleForComponent from 'ember-test-helpers/test-module-for-component';
import TestModuleForModel from 'ember-test-helpers/test-module-for-model';
import TestModuleForIntegration from 'ember-test-helpers/test-module-for-integration';
import { getContext, setContext } from 'ember-test-helpers/test-context';
import { setResolver } from 'ember-test-helpers/test-resolver';

Expand All @@ -14,7 +13,6 @@ export {
TestModule,
TestModuleForComponent,
TestModuleForModel,
TestModuleForIntegration,
getContext,
setContext,
setResolver
Expand Down
14 changes: 11 additions & 3 deletions lib/ember-test-helpers/isolated-container.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@ function exposeRegistryMethodsWithoutDeprecations(container) {
}
}

export default function isolatedContainer(fullNames) {
export function isolatedRegistry(fullNames) {
var resolver = getResolver();
var container;
var registry;

var normalize = function(fullName) {
return resolver.normalize(fullName);
};

if (Ember.Registry) {
var registry = new Ember.Registry();
registry = new Ember.Registry();
registry.normalizeFullName = normalize;

container = registry.container();
Expand Down Expand Up @@ -83,5 +84,12 @@ export default function isolatedContainer(fullNames) {
var normalizedFullName = resolver.normalize(fullName);
container.register(fullName, resolver.resolve(normalizedFullName));
}
return container;
return {
container: container,
registry: registry
};
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wary of changing the return value of isolatedContainer here, especially given the likelihood of the container/registry reform work proceeding very soon (emberjs/rfcs#46). In other words, I don't want developers who are using isolatedContainer to need to refactor twice in a short time. Maybe the question should be whether isolatedContainer is really used outside of ember-mocha and ember-qunit?

@ef4 @rwjblue thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed that only ember-mocha and ember-qunit would be affected, but I could be wrong.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can just implement isolatedRegistry instead. isolatedContainer can keep the same public API, but get reimplemented in terms of isolatedRegistry.

(While also maintaining compatibility for old Embers that don't have a registry.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect! Thanks in advance @ef4 👍


export function isolatedContainer(fullNames) {
return isolatedRegistry(fullNames).container;
}
112 changes: 109 additions & 3 deletions lib/ember-test-helpers/test-module-for-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,45 @@ import { getResolver } from './test-resolver';

export default TestModule.extend({
init: function(componentName, description, callbacks) {
// Allow `description` to be omitted
if (!callbacks && typeof description === 'object') {
callbacks = description || {};
description = null;
}

this.componentName = componentName;

this._super.call(this, 'component:' + componentName, description, callbacks);
if (callbacks.needs || callbacks.unit || callbacks.integration === false) {
this.isUnitTest = true;
} else if (callbacks.integration) {
this.isUnitTest = false;
} else {
Ember.deprecate("the component:" + componentName + " test module is implicitly running in unit test mode, which will change to integration test mode by default in an upcoming version of ember-test-helpers. Add `unit: true` or a `needs:[]` list to explicitly opt in to unit test mode.");
this.isUnitTest = true;
}

if (!this.isUnitTest) {
callbacks.integration = true;
}

this.setupSteps.push(this.setupComponent);
if (description) {
this._super.call(this, 'component:' + componentName, description, callbacks);
} else {
this._super.call(this, 'component:' + componentName, callbacks);
}

if (this.isUnitTest) {
this.setupSteps.push(this.setupComponentUnitTest);
} else {
this.callbacks.subject = function() {
throw new Error("component integration tests do not support `subject()`.");
};
this.setupSteps.push(this.setupComponentIntegrationTest);
this.teardownSteps.push(this.teardownComponent);
}
},

setupComponent: function() {
setupComponentUnitTest: function() {
var _this = this;
var resolver = getResolver();
var container = this.container;
Expand Down Expand Up @@ -55,5 +86,80 @@ export default TestModule.extend({

return subject.$.apply(subject, arguments);
};
},

setupComponentIntegrationTest: function() {
var self = this;
var context = this.context;
context.dispatcher = Ember.EventDispatcher.create();
context.dispatcher.setup({}, '#ember-testing');
this.actionHooks = {};

context.render = function(template) {
if (!template) {
throw new Error("in a component integration test you must pass a template to `render()`");
}
if (Ember.isArray(template)) {
template = template.join('');
}
if (typeof template === 'string') {
template = Ember.Handlebars.compile(template);
}
self.component = Ember.View.create({
context: context,
controller: self,
template: template,
container: self.container
});
Ember.run(function() {
self.component.appendTo('#ember-testing');
});
};

context.$ = function() {
return self.component.$.apply(self.component, arguments);
};

context.set = function(key, value) {
Ember.run(function() {
Ember.set(context, key, value);
});
};

context.get = function(key) {
return Ember.get(context, key);
};

context.on = function(actionName, handler) {
self.actionHooks[actionName] = handler;
};

},

setupContext: function() {
this._super.call(this);
if (!this.isUnitTest) {
this.context.factory = function() {};
}
},


send: function(actionName) {
var hook = this.actionHooks[actionName];
if (!hook) {
throw new Error("integration testing template received unexpected action " + actionName);
}
hook.apply(this, Array.prototype.slice.call(arguments, 1));
},

teardownComponent: function() {
var component = this.component;
if (component) {
Ember.run(function() {
component.destroy();
});
}
}


});
89 changes: 0 additions & 89 deletions lib/ember-test-helpers/test-module-for-integration.js

This file was deleted.

7 changes: 5 additions & 2 deletions lib/ember-test-helpers/test-module.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Ember from 'ember';
import isolatedContainer from './isolated-container';
import { isolatedRegistry } from './isolated-container';
import { getContext, setContext } from './test-context';
import { Klass } from 'klassy';
import { getResolver } from './test-resolver';
Expand Down Expand Up @@ -133,6 +133,7 @@ export default Klass.extend({

setContext({
container: this.container,
registry: this.registry,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

factory: factory,
dispatcher: null
});
Expand Down Expand Up @@ -213,7 +214,9 @@ export default Klass.extend({


_setupIsolatedContainer: function() {
this.container = isolatedContainer(this.needs);
var isolated = isolatedRegistry(this.needs);
this.container = isolated.container;
this.registry = isolated.registry;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes the isolated case consistent with the integrated case, which already provides this.registry.

},

_setupIntegratedContainer: function() {
Expand Down
3 changes: 3 additions & 0 deletions tests/test-module-for-component-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ test('clears out views from test to test', function() {
///////////////////////////////////////////////////////////////////////////////

moduleForComponent('pretty-color', {
unit: true,
beforeSetup: function() {
setupRegistry();
}
Expand Down Expand Up @@ -215,6 +216,7 @@ test("$", function(){
});

moduleForComponent('pretty-color', 'component:pretty-color -- this.render in setup', {
unit: true,
beforeSetup: function() {
setupRegistry();
},
Expand All @@ -236,6 +238,7 @@ test("className", function(){
});

moduleForComponent('boring-color', 'component:boring-color -- still in DOM in willDestroyElement', {
unit: true,
beforeSetup: function() {
setupRegistry();
},
Expand Down
25 changes: 21 additions & 4 deletions tests/test-module-for-integration-test.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import Ember from 'ember';
import { TestModuleForIntegration } from 'ember-test-helpers';
import { TestModuleForComponent } from 'ember-test-helpers';
import test from 'tests/test-support/qunit-test';
import qunitModuleFor from 'tests/test-support/qunit-module-for';
import { setResolverRegistry } from 'tests/test-support/resolver';

function moduleForIntegration(name, description, callbacks) {
var module = new TestModuleForIntegration(name, description, callbacks);
function moduleForComponent(name, description, callbacks) {
var module = new TestModuleForComponent(name, description, callbacks);
qunitModuleFor(module);
}

moduleForIntegration('Better Integration Tests', {

moduleForComponent('Component Integration Tests', {
integration: true,
beforeSetup: function() {
setResolverRegistry({
'template:components/my-component': Ember.Handlebars.compile(
Expand All @@ -24,6 +26,21 @@ test('it can render a template', function() {
equal(this.$('span').text(), 'Hello');
});

test('it complains if you try to use bare render', function() {
var self = this;
throws(function() {
self.render();
}, /in a component integration test you must pass a template to `render\(\)`/);
});

test('it complains if you try to use subject()', function() {
var self = this;
throws(function() {
self.subject();
}, /component integration tests do not support `subject\(\)`\./);
});


test('it can access the full container', function() {
this.set('myColor', 'red');
this.render('{{my-component name=myColor}}');
Expand Down