import { EMBER_GLIMMER_ANGLE_BRACKET_INVOCATION } from '@ember/canary-features';
import { moduleFor, RenderingTest } from '../../utils/test-case';
import { set } from 'ember-metal';
import { Component } from '../../utils/helpers';
import { strip } from '../../utils/abstract-test-case';
import { classes } from '../../utils/test-helpers';
if (EMBER_GLIMMER_ANGLE_BRACKET_INVOCATION) {
moduleFor(
'AngleBracket Invocation',
class extends RenderingTest {
'@test it can resolve to x-blah'() {
this.registerComponent('x-blah', { template: 'hello' });
this.render('');
this.assertComponentElement(this.firstChild, { content: 'hello' });
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, { content: 'hello' });
}
'@test it can resolve to x-blah'() {
this.registerComponent('x-blah', { template: 'hello' });
this.render('');
this.assertComponentElement(this.firstChild, { content: 'hello' });
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, { content: 'hello' });
}
'@test it can render a basic template only component'() {
this.registerComponent('foo-bar', { template: 'hello' });
this.render('');
this.assertComponentElement(this.firstChild, { content: 'hello' });
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, { content: 'hello' });
}
'@test it can render a basic component with template and javascript'() {
this.registerComponent('foo-bar', {
template: 'FIZZ BAR {{local}}',
ComponentClass: Component.extend({ local: 'hey' }),
});
this.render('');
this.assertComponentElement(this.firstChild, { content: 'FIZZ BAR hey' });
}
'@test it can render a single word component name'() {
this.registerComponent('foo', { template: 'hello' });
this.render('');
this.assertComponentElement(this.firstChild, { content: 'hello' });
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, { content: 'hello' });
}
'@test it can not render a component name without initial capital letter'(assert) {
this.registerComponent('div', {
ComponentClass: Component.extend({
init() {
assert.ok(false, 'should not have created component');
},
}),
});
this.render('
');
this.assertElement(this.firstChild, { tagName: 'div', content: '' });
}
'@test it can have a custom id and it is not bound'() {
this.registerComponent('foo-bar', { template: '{{id}} {{elementId}}' });
this.render('', {
customId: 'bizz',
});
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: { id: 'bizz' },
content: 'bizz bizz',
});
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: { id: 'bizz' },
content: 'bizz bizz',
});
this.runTask(() => set(this.context, 'customId', 'bar'));
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: { id: 'bizz' },
content: 'bar bizz',
});
this.runTask(() => set(this.context, 'customId', 'bizz'));
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: { id: 'bizz' },
content: 'bizz bizz',
});
}
'@test it can have a custom tagName'() {
let FooBarComponent = Component.extend({
tagName: 'foo-bar',
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: 'hello',
});
this.render('');
this.assertComponentElement(this.firstChild, {
tagName: 'foo-bar',
content: 'hello',
});
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, {
tagName: 'foo-bar',
content: 'hello',
});
}
'@test it can have a custom tagName from the invocation'() {
this.registerComponent('foo-bar', { template: 'hello' });
this.render('');
this.assertComponentElement(this.firstChild, {
tagName: 'foo-bar',
content: 'hello',
});
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, {
tagName: 'foo-bar',
content: 'hello',
});
}
'@test it can have custom classNames'() {
let FooBarComponent = Component.extend({
classNames: ['foo', 'bar'],
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: 'hello',
});
this.render('');
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: { class: classes('ember-view foo bar') },
content: 'hello',
});
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: { class: classes('ember-view foo bar') },
content: 'hello',
});
}
'@test class property on components can be dynamic'() {
this.registerComponent('foo-bar', { template: 'hello' });
this.render('', {
fooBar: true,
});
this.assertComponentElement(this.firstChild, {
content: 'hello',
attrs: { class: classes('ember-view foo-bar') },
});
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, {
content: 'hello',
attrs: { class: classes('ember-view foo-bar') },
});
this.runTask(() => set(this.context, 'fooBar', false));
this.assertComponentElement(this.firstChild, {
content: 'hello',
attrs: { class: classes('ember-view') },
});
this.runTask(() => set(this.context, 'fooBar', true));
this.assertComponentElement(this.firstChild, {
content: 'hello',
attrs: { class: classes('ember-view foo-bar') },
});
}
'@test it can set custom classNames from the invocation'() {
let FooBarComponent = Component.extend({
classNames: ['foo'],
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: 'hello',
});
this.render(strip`
`);
this.assertComponentElement(this.nthChild(0), {
tagName: 'div',
attrs: { class: classes('ember-view foo bar baz') },
content: 'hello',
});
this.assertComponentElement(this.nthChild(1), {
tagName: 'div',
attrs: { class: classes('ember-view foo bar baz') },
content: 'hello',
});
this.assertComponentElement(this.nthChild(2), {
tagName: 'div',
attrs: { class: classes('ember-view foo') },
content: 'hello',
});
this.runTask(() => this.rerender());
this.assertComponentElement(this.nthChild(0), {
tagName: 'div',
attrs: { class: classes('ember-view foo bar baz') },
content: 'hello',
});
this.assertComponentElement(this.nthChild(1), {
tagName: 'div',
attrs: { class: classes('ember-view foo bar baz') },
content: 'hello',
});
this.assertComponentElement(this.nthChild(2), {
tagName: 'div',
attrs: { class: classes('ember-view foo') },
content: 'hello',
});
}
'@test it has an element'() {
let instance;
let FooBarComponent = Component.extend({
init() {
this._super();
instance = this;
},
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: 'hello',
});
this.render('');
let element1 = instance.element;
this.assertComponentElement(element1, { content: 'hello' });
this.runTask(() => this.rerender());
let element2 = instance.element;
this.assertComponentElement(element2, { content: 'hello' });
this.assertSameNode(element2, element1);
}
'@test it has the right parentView and childViews'(assert) {
let fooBarInstance, fooBarBazInstance;
let FooBarComponent = Component.extend({
init() {
this._super();
fooBarInstance = this;
},
});
let FooBarBazComponent = Component.extend({
init() {
this._super();
fooBarBazInstance = this;
},
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: 'foo-bar {{foo-bar-baz}}',
});
this.registerComponent('foo-bar-baz', {
ComponentClass: FooBarBazComponent,
template: 'foo-bar-baz',
});
this.render('');
this.assertText('foo-bar foo-bar-baz');
assert.equal(fooBarInstance.parentView, this.component);
assert.equal(fooBarBazInstance.parentView, fooBarInstance);
assert.deepEqual(this.component.childViews, [fooBarInstance]);
assert.deepEqual(fooBarInstance.childViews, [fooBarBazInstance]);
this.runTask(() => this.rerender());
this.assertText('foo-bar foo-bar-baz');
assert.equal(fooBarInstance.parentView, this.component);
assert.equal(fooBarBazInstance.parentView, fooBarInstance);
assert.deepEqual(this.component.childViews, [fooBarInstance]);
assert.deepEqual(fooBarInstance.childViews, [fooBarBazInstance]);
}
'@test it renders passed named arguments'() {
this.registerComponent('foo-bar', {
template: '{{@foo}}',
});
this.render('', {
model: {
bar: 'Hola',
},
});
this.assertText('Hola');
this.runTask(() => this.rerender());
this.assertText('Hola');
this.runTask(() => this.context.set('model.bar', 'Hello'));
this.assertText('Hello');
this.runTask(() => this.context.set('model', { bar: 'Hola' }));
this.assertText('Hola');
}
'@test it reflects named arguments as properties'() {
this.registerComponent('foo-bar', {
template: '{{foo}}',
});
this.render('', {
model: {
bar: 'Hola',
},
});
this.assertText('Hola');
this.runTask(() => this.rerender());
this.assertText('Hola');
this.runTask(() => this.context.set('model.bar', 'Hello'));
this.assertText('Hello');
this.runTask(() => this.context.set('model', { bar: 'Hola' }));
this.assertText('Hola');
}
'@test it can render a basic component with a block'() {
this.registerComponent('foo-bar', {
template: '{{yield}} - In component',
});
this.render('hello');
this.assertComponentElement(this.firstChild, {
content: 'hello - In component',
});
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, {
content: 'hello - In component',
});
}
'@test it can yield internal and external properties positionally'() {
let instance;
let FooBarComponent = Component.extend({
init() {
this._super(...arguments);
instance = this;
},
greeting: 'hello',
});
this.registerComponent('foo-bar', {
ComponentClass: FooBarComponent,
template: '{{yield greeting greetee.firstName}}',
});
this.render(
'{{name}} {{person.lastName}}, {{greeting}}',
{
person: {
firstName: 'Joel',
lastName: 'Kang',
},
}
);
this.assertComponentElement(this.firstChild, {
content: 'Joel Kang, hello',
});
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, {
content: 'Joel Kang, hello',
});
this.runTask(() =>
set(this.context, 'person', {
firstName: 'Dora',
lastName: 'the Explorer',
})
);
this.assertComponentElement(this.firstChild, {
content: 'Dora the Explorer, hello',
});
this.runTask(() => set(instance, 'greeting', 'hola'));
this.assertComponentElement(this.firstChild, {
content: 'Dora the Explorer, hola',
});
this.runTask(() => {
set(instance, 'greeting', 'hello');
set(this.context, 'person', {
firstName: 'Joel',
lastName: 'Kang',
});
});
this.assertComponentElement(this.firstChild, {
content: 'Joel Kang, hello',
});
}
'@test positional parameters are not allowed'() {
this.registerComponent('sample-component', {
ComponentClass: Component.extend().reopenClass({
positionalParams: ['first', 'second'],
}),
template: '{{first}}{{second}}',
});
// this is somewhat silly as the browser "corrects" for these as
// attribute names, but regardless the thing we care about here is that
// they are **not** used as positional params
this.render('');
this.assertText('');
}
'@test can invoke curried components with capitalized block param names'() {
this.registerComponent('foo-bar', { template: 'hello' });
this.render(strip`
{{#with (component 'foo-bar') as |Other|}}
{{/with}}
`);
this.assertComponentElement(this.firstChild, { content: 'hello' });
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, { content: 'hello' });
this.assertStableRerender();
}
'@test can invoke curried components with named args'() {
this.registerComponent('foo-bar', { template: 'hello' });
this.registerComponent('test-harness', { template: '<@foo />' });
this.render(strip`{{test-harness foo=(component 'foo-bar')}}`);
this.assertComponentElement(this.firstChild.firstChild, { content: 'hello' });
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild.firstChild, { content: 'hello' });
this.assertStableRerender();
}
'@test can invoke curried components with a path'() {
this.registerComponent('foo-bar', { template: 'hello' });
this.registerComponent('test-harness', { template: '' });
this.render(strip`{{test-harness foo=(component 'foo-bar')}}`);
this.assertComponentElement(this.firstChild.firstChild, { content: 'hello' });
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild.firstChild, { content: 'hello' });
this.assertStableRerender();
}
'@test can not invoke curried components with an implicit `this` path'(assert) {
assert.expect(0);
this.registerComponent('foo-bar', {
template: 'hello',
ComponentClass: Component.extend({
init() {
this._super(...arguments);
assert.ok(false, 'should not have instantiated');
},
}),
});
this.registerComponent('test-harness', {
template: '',
});
this.render(strip`{{test-harness foo=(hash bar=(component 'foo-bar'))}}`);
}
'@test has-block'() {
this.registerComponent('check-block', {
template: strip`
{{#if (has-block)}}
Yes
{{else}}
No
{{/if}}`,
});
this.render(strip`
`);
this.assertComponentElement(this.firstChild, { content: 'No' });
this.assertComponentElement(this.nthChild(1), { content: 'Yes' });
this.assertStableRerender();
}
'@test includes invocation specified attributes in root element ("splattributes")'() {
this.registerComponent('foo-bar', {
ComponentClass: Component.extend(),
template: 'hello',
});
this.render('', { foo: 'foo', bar: 'bar' });
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
this.runTask(() => this.rerender());
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
this.runTask(() => {
set(this.context, 'foo', 'FOO');
set(this.context, 'bar', undefined);
});
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'FOO' },
content: 'hello',
});
this.runTask(() => {
set(this.context, 'foo', 'foo');
set(this.context, 'bar', 'bar');
});
this.assertComponentElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
}
'@test includes invocation specified attributes in `...attributes` slot in tagless component ("splattributes")'() {
this.registerComponent('foo-bar', {
ComponentClass: Component.extend({ tagName: '' }),
template: 'hello
',
});
this.render('', { foo: 'foo', bar: 'bar' });
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
this.runTask(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
this.runTask(() => {
set(this.context, 'foo', 'FOO');
set(this.context, 'bar', undefined);
});
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'FOO' },
content: 'hello',
});
this.runTask(() => {
set(this.context, 'foo', 'foo');
set(this.context, 'bar', 'bar');
});
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
}
'@test merges attributes with `...attributes` in tagless component ("splattributes")'() {
let instance;
this.registerComponent('foo-bar', {
ComponentClass: Component.extend({
tagName: '',
init() {
instance = this;
this._super(...arguments);
this.localProp = 'qux';
},
}),
template: 'hello
',
});
this.render('', { foo: 'foo', bar: 'bar' });
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-derp': 'qux', 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
this.runTask(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-derp': 'qux', 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
this.runTask(() => {
set(this.context, 'foo', 'FOO');
set(this.context, 'bar', undefined);
set(instance, 'localProp', 'QUZ');
});
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-derp': 'QUZ', 'data-foo': 'FOO' },
content: 'hello',
});
this.runTask(() => {
set(this.context, 'foo', 'foo');
set(this.context, 'bar', 'bar');
set(instance, 'localProp', 'qux');
});
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-derp': 'qux', 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
}
'@test merges class attribute with `...attributes` in tagless component ("splattributes")'() {
let instance;
this.registerComponent('foo-bar', {
ComponentClass: Component.extend({
tagName: '',
init() {
instance = this;
this._super(...arguments);
this.localProp = 'qux';
},
}),
template: 'hello
',
});
this.render('', { bar: 'bar' });
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { class: classes('qux bar') },
content: 'hello',
});
this.runTask(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { class: classes('qux bar') },
content: 'hello',
});
this.runTask(() => {
set(this.context, 'bar', undefined);
set(instance, 'localProp', 'QUZ');
});
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { class: classes('QUZ') },
content: 'hello',
});
this.runTask(() => {
set(this.context, 'bar', 'bar');
set(instance, 'localProp', 'qux');
});
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { class: classes('qux bar') },
content: 'hello',
});
}
'@test can include `...attributes` in multiple elements in tagless component ("splattributes")'() {
this.registerComponent('foo-bar', {
ComponentClass: Component.extend({ tagName: '' }),
template: 'hello
world
',
});
this.render('', { foo: 'foo', bar: 'bar' });
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
this.assertElement(this.nthChild(1), {
tagName: 'p',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'world',
});
this.runTask(() => this.rerender());
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
this.assertElement(this.nthChild(1), {
tagName: 'p',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'world',
});
this.runTask(() => {
set(this.context, 'foo', 'FOO');
set(this.context, 'bar', undefined);
});
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'FOO' },
content: 'hello',
});
this.assertElement(this.nthChild(1), {
tagName: 'p',
attrs: { 'data-foo': 'FOO' },
content: 'world',
});
this.runTask(() => {
set(this.context, 'foo', 'foo');
set(this.context, 'bar', 'bar');
});
this.assertElement(this.firstChild, {
tagName: 'div',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'hello',
});
this.assertElement(this.nthChild(1), {
tagName: 'p',
attrs: { 'data-foo': 'foo', 'data-bar': 'bar' },
content: 'world',
});
}
}
);
}