diff --git a/packages/ember-glimmer/tests/integration/components/curly-components-test.js b/packages/ember-glimmer/tests/integration/components/curly-components-test.js index 90d6a71f047..236b6cdc16c 100644 --- a/packages/ember-glimmer/tests/integration/components/curly-components-test.js +++ b/packages/ember-glimmer/tests/integration/components/curly-components-test.js @@ -2897,6 +2897,59 @@ moduleFor('Components test: curly components', class extends RenderingTest { assert.strictEqual(barCopyDidChangeCount, 1, 'expected observer firing for: barCopy'); } + ['@test can access didInitAttrs arguments [DEPRECATED]'](assert) { + expectDeprecation(/didInitAttrs called/); + + this.registerComponent('foo-bar', { + ComponentClass: Component.extend({ + init() { + this._super(...arguments); + }, + + didInitAttrs({ attrs }) { + assert.ok(this.didInit, 'expected init to have run before didInitAttrs'); + this.set('fooCopy', attrs.foo.value + 1); + } + }), + + template: '{{foo}}-{{fooCopy}}-{{bar}}-{{barCopy}}' + }); + + this.render(`{{foo-bar foo=foo bar=bar}}`, { foo: 1, bar: 3 }); + } + + ['@test can access did{Receive,Update}Attrs arguments [DEPRECATED]'](assert) { + // expectDeprecation(/didInitAttrs called/); + + this.registerComponent('foo-bar', { + ComponentClass: Component.extend({ + init() { + this._super(...arguments); + this.didInit = true; + }, + + didReceiveAttrs({ attrs }) { + assert.ok(this.didInit, 'expected init to have run before didInitAttrs'); + this.set('fooCopy', attrs.foo.value + 1); + }, + + didReceiveAttrs({ newAttrs }) { + assert.ok(this.didInit, 'expected init to have run before didReceiveAttrs'); + this.set('barCopy', newAttrs.bar.value + 1); + }, + + fooCopyDidChange: observer('fooCopy', () => { fooCopyDidChangeCount++; }), + barCopyDidChange: observer('barCopy', () => { barCopyDidChangeCount++; }) + }), + + template: '{{foo}}-{{fooCopy}}-{{bar}}-{{barCopy}}' + }); + + this.render(`{{foo-bar foo=foo bar=bar}}`, { foo: 1, bar: 3 }); + + this.runTask(() => set(this.context, 'foo', 5)); + } + ['@test returning `true` from an action does not bubble if `target` is not specified (GH#14275)'](assert) { this.registerComponent('display-toggle', { ComponentClass: Component.extend({ diff --git a/packages/ember-glimmer/tests/integration/syntax/let-test.js b/packages/ember-glimmer/tests/integration/syntax/let-test.js new file mode 100644 index 00000000000..6229aa11937 --- /dev/null +++ b/packages/ember-glimmer/tests/integration/syntax/let-test.js @@ -0,0 +1,395 @@ +import { get, set } from 'ember-metal'; +import { A as emberA, ObjectProxy, removeAt } from 'ember-runtime'; +import { moduleFor, RenderingTest } from '../../utils/test-case'; +import { IfUnlessWithSyntaxTest } from '../../utils/shared-conditional-tests'; +import { strip } from '../../utils/abstract-test-case'; + +moduleFor('Syntax test: {{#with}}', class extends IfUnlessWithSyntaxTest { + + templateFor({ cond, truthy, falsy }) { + return `{{#with ${cond}}}${truthy}{{else}}${falsy}{{/with}}`; + } + +}); + +moduleFor('Syntax test: {{#with as}}', class extends IfUnlessWithSyntaxTest { + + templateFor({ cond, truthy, falsy }) { + return `{{#with ${cond} as |test|}}${truthy}{{else}}${falsy}{{/with}}`; + } + + ['@test keying off of `undefined` does not render'](assert) { + this.render(strip` + {{#with foo.bar.baz as |thing|}} + {{thing}} + {{/with}}`, { foo: {} }); + + this.assertText(''); + + this.runTask(() => this.rerender()); + + this.assertText(''); + + this.runTask(() => set(this.context, 'foo', { bar: { baz: 'Here!' } })); + + this.assertText('Here!'); + + this.runTask(() => set(this.context, 'foo', {})); + + this.assertText(''); + } + + ['@test it renders and hides the given block based on the conditional']() { + this.render(`{{#with cond1 as |cond|}}{{cond.greeting}}{{else}}False{{/with}}`, { + cond1: { greeting: 'Hello' } + }); + + this.assertText('Hello'); + + this.runTask(() => this.rerender()); + + this.assertText('Hello'); + + this.runTask(() => set(this.context, 'cond1.greeting', 'Hello world')); + + this.assertText('Hello world'); + + this.runTask(() => set(this.context, 'cond1', false)); + + this.assertText('False'); + + this.runTask(() => set(this.context, 'cond1', { greeting: 'Hello' })); + + this.assertText('Hello'); + } + + ['@test can access alias and original scope']() { + this.render(`{{#with person as |tom|}}{{title}}: {{tom.name}}{{/with}}`, { + title: 'Señor Engineer', + person: { name: 'Tom Dale' } + }); + + this.assertText('Señor Engineer: Tom Dale'); + + this.runTask(() => this.rerender()); + + this.assertText('Señor Engineer: Tom Dale'); + + this.runTask(() => { + set(this.context, 'person.name', 'Yehuda Katz'); + set(this.context, 'title', 'Principal Engineer'); + }); + + this.assertText('Principal Engineer: Yehuda Katz'); + + this.runTask(() => { + set(this.context, 'person', { name: 'Tom Dale' }); + set(this.context, 'title', 'Señor Engineer'); + }); + + this.assertText('Señor Engineer: Tom Dale'); + } + + ['@test the scoped variable is not available outside the {{#with}} block.']() { + this.render(`{{name}}-{{#with other as |name|}}{{name}}{{/with}}-{{name}}`, { + name: 'Stef', + other: 'Yehuda' + }); + + this.assertText('Stef-Yehuda-Stef'); + + this.runTask(() => this.rerender()); + + this.assertText('Stef-Yehuda-Stef'); + + this.runTask(() => set(this.context, 'other', 'Chad')); + + this.assertText('Stef-Chad-Stef'); + + this.runTask(() => set(this.context, 'name', 'Tom')); + + this.assertText('Tom-Chad-Tom'); + + this.runTask(() => { + set(this.context, 'name', 'Stef'); + set(this.context, 'other', 'Yehuda'); + }); + + this.assertText('Stef-Yehuda-Stef'); + } + + ['@test inverse template is displayed with context']() { + this.render(`{{#with falsyThing as |thing|}}Has Thing{{else}}No Thing {{otherThing}}{{/with}}`, { + falsyThing: null, + otherThing: 'bar' + }); + + this.assertText('No Thing bar'); + + this.runTask(() => this.rerender()); + + this.assertText('No Thing bar'); + + this.runTask(() => set(this.context, 'otherThing', 'biz')); + + this.assertText('No Thing biz'); + + this.runTask(() => set(this.context, 'falsyThing', true)); + + this.assertText('Has Thing'); + + this.runTask(() => set(this.context, 'otherThing', 'baz')); + + this.assertText('Has Thing'); + + this.runTask(() => { + set(this.context, 'otherThing', 'bar'); + set(this.context, 'falsyThing', null); + }); + + this.assertText('No Thing bar'); + } + + ['@test can access alias of a proxy']() { + this.render(`{{#with proxy as |person|}}{{person.name}}{{/with}}`, { + proxy: ObjectProxy.create({ content: { name: 'Tom Dale' } }) + }); + + this.assertText('Tom Dale'); + + this.runTask(() => this.rerender()); + + this.assertText('Tom Dale'); + + this.runTask(() => set(this.context, 'proxy.name', 'Yehuda Katz')); + + this.assertText('Yehuda Katz'); + + this.runTask(() => set(this.context, 'proxy.content', { name: 'Godfrey Chan' })); + + this.assertText('Godfrey Chan'); + + this.runTask(() => set(this.context, 'proxy.content.name', 'Stefan Penner')); + + this.assertText('Stefan Penner'); + + this.runTask(() => set(this.context, 'proxy.content', null)); + + this.assertText(''); + + this.runTask(() => set(this.context, 'proxy', ObjectProxy.create({ content: { name: 'Tom Dale' } }))); + + this.assertText('Tom Dale'); + } + + ['@test can access alias of an array']() { + this.render(`{{#with arrayThing as |words|}}{{#each words as |word|}}{{word}}{{/each}}{{/with}}`, { + arrayThing: emberA(['Hello', ' ', 'world']) + }); + + this.assertText('Hello world'); + + this.runTask(() => this.rerender()); + + this.assertText('Hello world'); + + this.runTask(() => { + let array = get(this.context, 'arrayThing'); + array.replace(0, 1, 'Goodbye'); + removeAt(array, 1); + array.insertAt(1, ', '); + array.pushObject('!'); + }); + + this.assertText('Goodbye, world!'); + + this.runTask(() => set(this.context, 'arrayThing', ['Hello', ' ', 'world'])); + + this.assertText('Hello world'); + } + + ['@test `attrs` can be used as a block param [GH#14678]']() { + this.render('{{#with hash as |attrs|}}[{{hash.foo}}-{{attrs.foo}}]{{/with}}', { + hash: { foo: 'foo' } + }); + + this.assertText('[foo-foo]'); + + this.runTask(() => this.rerender()); + + this.assertText('[foo-foo]'); + + this.runTask(() => this.context.set('hash.foo', 'FOO')); + + this.assertText('[FOO-FOO]'); + + this.runTask(() => this.context.set('hash.foo', 'foo')); + + this.assertText('[foo-foo]'); + } + +}); + +moduleFor('Syntax test: Multiple {{#with as}} helpers', class extends RenderingTest { + + ['@test re-using the same variable with different {{#with}} blocks does not override each other']() { + this.render(`Admin: {{#with admin as |person|}}{{person.name}}{{/with}} User: {{#with user as |person|}}{{person.name}}{{/with}}`, { + admin: { name: 'Tom Dale' }, + user: { name: 'Yehuda Katz' } + }); + + this.assertText('Admin: Tom Dale User: Yehuda Katz'); + + this.runTask(() => this.rerender()); + + this.assertText('Admin: Tom Dale User: Yehuda Katz'); + + this.runTask(() => { + set(this.context, 'admin.name', 'Godfrey Chan'); + set(this.context, 'user.name', 'Stefan Penner'); + }); + + this.assertText('Admin: Godfrey Chan User: Stefan Penner'); + + this.runTask(() => { + set(this.context, 'admin', { name: 'Tom Dale' }); + set(this.context, 'user', { name: 'Yehuda Katz' }); + }); + + this.assertText('Admin: Tom Dale User: Yehuda Katz'); + } + + ['@test the scoped variable is not available outside the {{#with}} block']() { + this.render(`{{ring}}-{{#with first as |ring|}}{{ring}}-{{#with fifth as |ring|}}{{ring}}-{{#with ninth as |ring|}}{{ring}}-{{/with}}{{ring}}-{{/with}}{{ring}}-{{/with}}{{ring}}`, { + ring: 'Greed', + first: 'Limbo', + fifth: 'Wrath', + ninth: 'Treachery' + }); + + this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed'); + + this.runTask(() => this.rerender()); + + this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed'); + + this.runTask(() => { + set(this.context, 'ring', 'O'); + set(this.context, 'fifth', 'D'); + }); + + this.assertText('O-Limbo-D-Treachery-D-Limbo-O'); + + this.runTask(() => { + set(this.context, 'first', 'I'); + set(this.context, 'ninth', 'K'); + }); + + this.assertText('O-I-D-K-D-I-O'); + + this.runTask(() => { + set(this.context, 'ring', 'Greed'); + set(this.context, 'first', 'Limbo'); + set(this.context, 'fifth', 'Wrath'); + set(this.context, 'ninth', 'Treachery'); + }); + + this.assertText('Greed-Limbo-Wrath-Treachery-Wrath-Limbo-Greed'); + } + + ['@test it should support {{#with name as |foo|}}, then {{#with foo as |bar|}}']() { + this.render(`{{#with name as |foo|}}{{#with foo as |bar|}}{{bar}}{{/with}}{{/with}}`, { + name: 'caterpillar' + }); + + this.assertText('caterpillar'); + + this.runTask(() => this.rerender()); + + this.assertText('caterpillar'); + + this.runTask(() => set(this.context, 'name', 'butterfly')); + + this.assertText('butterfly'); + + this.runTask(() => set(this.context, 'name', 'caterpillar')); + + this.assertText('caterpillar'); + } + + ['@test updating the context should update the alias']() { + this.render(`{{#with this as |person|}}{{person.name}}{{/with}}`, { + name: 'Los Pivots' + }); + + this.assertText('Los Pivots'); + + this.runTask(() => this.rerender()); + + this.assertText('Los Pivots'); + + this.runTask(() => set(this.context, 'name', 'l\'Pivots')); + + this.assertText('l\'Pivots'); + + this.runTask(() => set(this.context, 'name', 'Los Pivots')); + + this.assertText('Los Pivots'); + } + + ['@test nested {{#with}} blocks should have access to root context']() { + this.render(strip` + {{name}} + {{#with committer1.name as |name|}} + [{{name}} + {{#with committer2.name as |name|}} + [{{name}}] + {{/with}} + {{name}}] + {{/with}} + {{name}} + {{#with committer2.name as |name|}} + [{{name}} + {{#with committer1.name as |name|}} + [{{name}}] + {{/with}} + {{name}}] + {{/with}} + {{name}} + `, { + name: 'ebryn', + committer1: { name: 'trek' }, + committer2: { name: 'machty' } + }); + + this.assertText('ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn'); + + this.runTask(() => this.rerender()); + + this.assertText('ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn'); + + this.runTask(() => set(this.context, 'name', 'chancancode')); + + this.assertText('chancancode[trek[machty]trek]chancancode[machty[trek]machty]chancancode'); + + this.runTask(() => set(this.context, 'committer1', { name: 'krisselden' })); + + this.assertText('chancancode[krisselden[machty]krisselden]chancancode[machty[krisselden]machty]chancancode'); + + this.runTask(() => { + set(this.context, 'committer1.name', 'wycats'); + set(this.context, 'committer2', { name: 'rwjblue' }); + }); + + this.assertText('chancancode[wycats[rwjblue]wycats]chancancode[rwjblue[wycats]rwjblue]chancancode'); + + this.runTask(() => { + set(this.context, 'name', 'ebryn'); + set(this.context, 'committer1', { name: 'trek' }); + set(this.context, 'committer2', { name: 'machty' }); + }); + + this.assertText('ebryn[trek[machty]trek]ebryn[machty[trek]machty]ebryn'); + } + +}); diff --git a/packages/ember-views/lib/mixins/view_support.js b/packages/ember-views/lib/mixins/view_support.js index 02f318750ab..ac28b32f33a 100644 --- a/packages/ember-views/lib/mixins/view_support.js +++ b/packages/ember-views/lib/mixins/view_support.js @@ -27,7 +27,7 @@ runInDebug(() => { deprecate(this._message, false, { id: 'ember-views.lifecycle-hook-arguments', until: '2.13.0', - url: 'TODO' + url: 'http://emberjs.com/deprecations/v2.x/#toc_arguments-in-component-lifecycle-hooks' }); return this._oldAttrs; @@ -37,7 +37,7 @@ runInDebug(() => { deprecate(this._message, false, { id: 'ember-views.lifecycle-hook-arguments', until: '2.13.0', - url: 'TODO' + url: 'http://emberjs.com/deprecations/v2.x/#toc_arguments-in-component-lifecycle-hooks' }); return this._newAttrs; @@ -513,7 +513,7 @@ export default Mixin.create({ { id: 'ember-views.lifecycle-hook-arguments', until: '2.13.0', - url: 'TODO' + url: 'http://emberjs.com/deprecations/v2.x/#toc_arguments-in-component-lifecycle-hooks' } ); @@ -523,7 +523,7 @@ export default Mixin.create({ { id: 'ember-views.lifecycle-hook-arguments', until: '2.13.0', - url: 'TODO' + url: 'http://emberjs.com/deprecations/v2.x/#toc_arguments-in-component-lifecycle-hooks' } ); @@ -533,7 +533,7 @@ export default Mixin.create({ { id: 'ember-views.lifecycle-hook-arguments', until: '2.13.0', - url: 'TODO' + url: 'http://emberjs.com/deprecations/v2.x/#toc_arguments-in-component-lifecycle-hooks' } );