import { getOwner } from 'ember-owner';
import { moduleFor, ApplicationTest, RenderingTest } from '../utils/test-case';
import { compile, Component } from '../utils/helpers';
import Controller from '@ember/controller';
import { set } from 'ember-metal';
import Engine, { getEngineParent } from '@ember/engine';
import { EMBER_ENGINES_MOUNT_PARAMS } from '@ember/canary-features';
if (EMBER_ENGINES_MOUNT_PARAMS) {
moduleFor(
'{{mount}} single param assertion',
class extends RenderingTest {
['@test it asserts that only a single param is passed']() {
expectAssertion(() => {
this.render('{{mount "chat" "foo"}}');
}, /You can only pass a single positional argument to the {{mount}} helper, e.g. {{mount "chat-engine"}}./i);
}
}
);
} else {
moduleFor(
'{{mount}} single param assertion',
class extends RenderingTest {
['@test it asserts that only a single param is passed']() {
expectAssertion(() => {
this.render('{{mount "chat" "foo"}}');
}, /You can only pass a single argument to the {{mount}} helper, e.g. {{mount "chat-engine"}}./i);
}
}
);
}
moduleFor(
'{{mount}} assertions',
class extends RenderingTest {
['@test it asserts when an invalid engine name is provided']() {
expectAssertion(() => {
this.render('{{mount engineName}}', { engineName: {} });
}, /Invalid engine name '\[object Object\]' specified, engine name must be either a string, null or undefined./i);
}
['@test it asserts that the specified engine is registered']() {
expectAssertion(() => {
this.render('{{mount "chat"}}');
}, /You used `{{mount 'chat'}}`, but the engine 'chat' can not be found./i);
}
}
);
moduleFor(
'{{mount}} test',
class extends ApplicationTest {
constructor() {
super(...arguments);
let engineRegistrations = (this.engineRegistrations = {});
this.add(
'engine:chat',
Engine.extend({
router: null,
init() {
this._super(...arguments);
Object.keys(engineRegistrations).forEach(fullName => {
this.register(fullName, engineRegistrations[fullName]);
});
},
})
);
this.addTemplate('index', '{{mount "chat"}}');
}
['@test it boots an engine, instantiates its application controller, and renders its application template'](
assert
) {
this.engineRegistrations['template:application'] = compile(
'
Chat here, {{username}}
',
{ moduleName: 'my-app/templates/application.hbs' }
);
let controller;
this.engineRegistrations['controller:application'] = Controller.extend({
username: 'dgeb',
init() {
this._super();
controller = this;
},
});
return this.visit('/').then(() => {
assert.ok(controller, "engine's application controller has been instantiated");
let engineInstance = getOwner(controller);
assert.strictEqual(
getEngineParent(engineInstance),
this.applicationInstance,
'engine instance has the application instance as its parent'
);
this.assertInnerHTML('Chat here, dgeb
');
this.runTask(() => set(controller, 'username', 'chancancode'));
this.assertInnerHTML('Chat here, chancancode
');
this.runTask(() => set(controller, 'username', 'dgeb'));
this.assertInnerHTML('Chat here, dgeb
');
});
}
['@test it emits a useful backtracking re-render assertion message']() {
this.router.map(function() {
this.route('route-with-mount');
});
this.addTemplate('index', '');
this.addTemplate('route-with-mount', '{{mount "chat"}}');
this.engineRegistrations['template:application'] = compile(
'hi {{person.name}} [{{component-with-backtracking-set person=person}}]',
{ moduleName: 'my-app/templates/application.hbs' }
);
this.engineRegistrations['controller:application'] = Controller.extend({
person: { name: 'Alex' },
});
this.engineRegistrations['template:components/component-with-backtracking-set'] = compile(
'[component {{person.name}}]',
{
moduleName: 'my-app/templates/components/component-with-backtracking-set.hbs',
}
);
this.engineRegistrations['component:component-with-backtracking-set'] = Component.extend({
init() {
this._super(...arguments);
this.set('person.name', 'Ben');
},
});
let expectedBacktrackingMessage = /modified "person\.name" twice on \[object Object\] in a single render\. It was rendered in "template:my-app\/templates\/route-with-mount.hbs" \(in "engine:chat"\) and modified in "component:component-with-backtracking-set" \(in "engine:chat"\)/;
return this.visit('/').then(() => {
expectAssertion(() => {
this.visit('/route-with-mount');
}, expectedBacktrackingMessage);
});
}
['@test it renders with a bound engine name']() {
this.router.map(function() {
this.route('bound-engine-name');
});
let controller;
this.add(
'controller:bound-engine-name',
Controller.extend({
engineName: null,
init() {
this._super();
controller = this;
},
})
);
this.addTemplate('bound-engine-name', '{{mount engineName}}');
this.add(
'engine:foo',
Engine.extend({
router: null,
init() {
this._super(...arguments);
this.register(
'template:application',
compile('Foo Engine
', {
moduleName: 'my-app/templates/application.hbs',
})
);
},
})
);
this.add(
'engine:bar',
Engine.extend({
router: null,
init() {
this._super(...arguments);
this.register(
'template:application',
compile('Bar Engine
', {
moduleName: 'my-app/templates/application.hbs',
})
);
},
})
);
return this.visit('/bound-engine-name').then(() => {
this.assertInnerHTML('');
this.runTask(() => set(controller, 'engineName', 'foo'));
this.assertInnerHTML('Foo Engine
');
this.runTask(() => set(controller, 'engineName', undefined));
this.assertInnerHTML('');
this.runTask(() => set(controller, 'engineName', 'foo'));
this.assertInnerHTML('Foo Engine
');
this.runTask(() => set(controller, 'engineName', 'bar'));
this.assertInnerHTML('Bar Engine
');
this.runTask(() => set(controller, 'engineName', 'foo'));
this.assertInnerHTML('Foo Engine
');
this.runTask(() => set(controller, 'engineName', null));
this.assertInnerHTML('');
});
}
['@test it declares the event dispatcher as a singleton']() {
this.router.map(function() {
this.route('engine-event-dispatcher-singleton');
});
let controller;
let component;
this.add(
'controller:engine-event-dispatcher-singleton',
Controller.extend({
init() {
this._super(...arguments);
controller = this;
},
})
);
this.addTemplate('engine-event-dispatcher-singleton', '{{mount "foo"}}');
this.add(
'engine:foo',
Engine.extend({
router: null,
init() {
this._super(...arguments);
this.register(
'template:application',
compile('Foo Engine: {{tagless-component}}
', {
moduleName: 'my-app/templates/application.hbs',
})
);
this.register(
'component:tagless-component',
Component.extend({
tagName: '',
init() {
this._super(...arguments);
component = this;
},
})
);
this.register(
'template:components/tagless-component',
compile('Tagless Component', {
moduleName: 'my-app/templates/components/tagless-component.hbs',
})
);
},
})
);
return this.visit('/engine-event-dispatcher-singleton').then(() => {
this.assertInnerHTML('Foo Engine: Tagless Component
');
let controllerOwnerEventDispatcher = getOwner(controller).lookup('event_dispatcher:main');
let taglessComponentOwnerEventDispatcher = getOwner(component).lookup(
'event_dispatcher:main'
);
this.assert.strictEqual(
controllerOwnerEventDispatcher,
taglessComponentOwnerEventDispatcher
);
});
}
}
);
if (EMBER_ENGINES_MOUNT_PARAMS) {
moduleFor(
'{{mount}} params tests',
class extends ApplicationTest {
constructor() {
super(...arguments);
this.add(
'engine:paramEngine',
Engine.extend({
router: null,
init() {
this._super(...arguments);
this.register(
'template:application',
compile('Param Engine: {{model.foo}}
', {
moduleName: 'my-app/templates/application.hbs',
})
);
},
})
);
}
['@test it renders with static parameters']() {
this.router.map(function() {
this.route('engine-params-static');
});
this.addTemplate('engine-params-static', '{{mount "paramEngine" model=(hash foo="bar")}}');
return this.visit('/engine-params-static').then(() => {
this.assertInnerHTML('Param Engine: bar
');
});
}
['@test it renders with bound parameters']() {
this.router.map(function() {
this.route('engine-params-bound');
});
let controller;
this.add(
'controller:engine-params-bound',
Controller.extend({
boundParamValue: null,
init() {
this._super();
controller = this;
},
})
);
this.addTemplate(
'engine-params-bound',
'{{mount "paramEngine" model=(hash foo=boundParamValue)}}'
);
return this.visit('/engine-params-bound').then(() => {
this.assertInnerHTML('Param Engine:
');
this.runTask(() => set(controller, 'boundParamValue', 'bar'));
this.assertInnerHTML('Param Engine: bar
');
this.runTask(() => set(controller, 'boundParamValue', undefined));
this.assertInnerHTML('Param Engine:
');
this.runTask(() => set(controller, 'boundParamValue', 'bar'));
this.assertInnerHTML('Param Engine: bar
');
this.runTask(() => set(controller, 'boundParamValue', 'baz'));
this.assertInnerHTML('Param Engine: baz
');
this.runTask(() => set(controller, 'boundParamValue', 'bar'));
this.assertInnerHTML('Param Engine: bar
');
this.runTask(() => set(controller, 'boundParamValue', null));
this.assertInnerHTML('Param Engine:
');
});
}
['@test it renders contextual components passed as parameter values']() {
this.router.map(function() {
this.route('engine-params-contextual-component');
});
this.addComponent('foo-component', {
template: `foo-component rendered! - {{app-bar-component}}`,
});
this.addComponent('app-bar-component', {
ComponentClass: Component.extend({ tagName: '' }),
template: 'rendered app-bar-component from the app',
});
this.add(
'engine:componentParamEngine',
Engine.extend({
router: null,
init() {
this._super(...arguments);
this.register(
'template:application',
compile('{{model.foo}}', {
moduleName: 'my-app/templates/application.hbs',
})
);
},
})
);
this.addTemplate(
'engine-params-contextual-component',
'{{mount "componentParamEngine" model=(hash foo=(component "foo-component"))}}'
);
return this.visit('/engine-params-contextual-component').then(() => {
this.assertComponentElement(this.firstChild, {
content: 'foo-component rendered! - rendered app-bar-component from the app',
});
});
}
}
);
}