From f5c8990610dff77dbf3729106036e6e835d4c80c Mon Sep 17 00:00:00 2001 From: Chad Hietala Date: Sat, 6 Feb 2016 12:02:09 -0800 Subject: [PATCH] with-tests Adds glimmer2 with tests and also refactors the conditional tests to share the basic truthy and falsy tests. --- .../integration/syntax/if-unless-test.js | 442 +----------------- .../syntax/shared-conditional-tests.js | 436 +++++++++++++++++ .../tests/integration/syntax/with-test.js | 33 ++ 3 files changed, 475 insertions(+), 436 deletions(-) create mode 100644 packages/ember-glimmer/tests/integration/syntax/shared-conditional-tests.js create mode 100644 packages/ember-glimmer/tests/integration/syntax/with-test.js diff --git a/packages/ember-glimmer/tests/integration/syntax/if-unless-test.js b/packages/ember-glimmer/tests/integration/syntax/if-unless-test.js index f81dae71a4f..ae88f3c434d 100644 --- a/packages/ember-glimmer/tests/integration/syntax/if-unless-test.js +++ b/packages/ember-glimmer/tests/integration/syntax/if-unless-test.js @@ -1,440 +1,10 @@ -import { RenderingTest, moduleFor } from '../../utils/test-case'; -import { get } from 'ember-metal/property_get'; +import { moduleFor } from '../../utils/test-case'; import { set } from 'ember-metal/property_set'; -import assign from 'ember-metal/assign'; -import EmberObject from 'ember-runtime/system/object'; -import ObjectProxy from 'ember-runtime/system/object_proxy'; -import { A as emberA } from 'ember-runtime/system/native_array'; -import ArrayProxy from 'ember-runtime/system/array_proxy'; - -class AbstractConditionalsTest extends RenderingTest { - - /* abstract */ - templateFor({ cond, truthy, falsy }) { - // e.g. `{{#if ${cond}}}${truthy}{{else}}${falsy}{{/if}}` - throw new Error('Not implemented: `templateFor`'); - } - - renderValues(...values) { - let templates = []; - let context = {}; - - for (let i = 1; i <= values.length; i++) { - templates.push(this.templateFor({ cond: `cond${i}`, truthy: `{{t}}${i}`, falsy: `{{f}}${i}` })); - context[`cond${i}`] = values[i - 1]; - } - - this.render(templates.join(''), assign({ t: 'T', f: 'F' }, context)); - } - -} - -/* - The test cases in this file generally follow the following pattern: - - 1. Render with [ truthy, ...(other truthy variations), falsy, ...(other falsy variations) ] - 2. No-op rerender - 3. Make all of them falsy (through interior mutation) - 4. Make all of them truthy (through interior mutation, sometimes with some slight variations) - 5. Reset them to their original values (through replacement) -*/ - -const BASIC_TRUTHY_TESTS = { - - cases: [ - true, - ' ', - 'hello', - 'false', - 'null', - 'undefined', - 1, - ['hello'], - emberA(['hello']), - {}, - { foo: 'bar' }, - EmberObject.create(), - EmberObject.create({ foo: 'bar' }), - /*jshint -W053 */ - new String('hello'), - new String(''), - new Boolean(true), - new Boolean(false), - new Date() - /*jshint +W053 */ - ], - - generate(value) { - return { - - [`@test it should consider ${JSON.stringify(value)} truthy`]() { - this.renderValues(value); - - this.assertText('T1'); - - this.runTask(() => this.rerender()); - - this.assertText('T1'); - - this.runTask(() => set(this.context, 'cond1', false)); - - this.assertText('F1'); - - this.runTask(() => set(this.context, 'cond1', value)); - - this.assertText('T1'); - } - - }; - } - -}; - -const BASIC_FALSY_TESTS = { - - cases: [ - false, - null, - undefined, - '', - 0, - [], - emberA() - ], - - generate(value) { - let tests = { - - [`@test it should consider ${JSON.stringify(value)} falsy`]() { - this.renderValues(value); - - this.assertText('F1'); - - this.runTask(() => this.rerender()); - - this.assertText('F1'); - - this.runTask(() => set(this.context, 'cond1', true)); - - this.assertText('T1'); - - this.runTask(() => set(this.context, 'cond1', value)); - - this.assertText('F1'); - } - - }; - - if (value !== false) { - // Only `{ isTruthy: false }` is falsy, `{ isTruthy: null }` etc are not - - tests[`@test it should consider { isTruthy: ${JSON.stringify(value)} } truthy`] = function() { - this.renderValues({ isTruthy: value }); - - this.assertText('T1'); - - this.runTask(() => this.rerender()); - - this.assertText('T1'); - - this.runTask(() => set(this.context, 'cond1.isTruthy', false)); - - this.assertText('F1'); - - this.runTask(() => set(this.context, 'cond1', { isTruthy: value })); - - this.assertText('T1'); - }; - } - } - -}; - -class SharedConditionalsTest extends AbstractConditionalsTest { - - ['@test it renders the corresponding block based on the conditional']() { - this.renderValues(true, false); - - this.assertText('T1F2'); - - this.runTask(() => this.rerender()); - - this.assertText('T1F2'); - - this.runTask(() => set(this.context, 'cond1', false)); - - this.assertText('F1F2'); - - this.runTask(() => { - set(this.context, 'cond1', true); - set(this.context, 'cond2', true); - }); - - this.assertText('T1T2'); - - this.runTask(() => { - set(this.context, 'cond1', true); - set(this.context, 'cond2', false); - }); - - this.assertText('T1F2'); - } - - ['@test it tests for `isTruthy` if available']() { - this.renderValues({ isTruthy: true }, { isTruthy: false }); - - this.assertText('T1F2'); - - this.runTask(() => this.rerender()); - - this.assertText('T1F2'); - - this.runTask(() => set(this.context, 'cond1.isTruthy', false)); - - this.assertText('F1F2'); - - this.runTask(() => { - set(this.context, 'cond1.isTruthy', true); - set(this.context, 'cond2.isTruthy', true); - }); - - this.assertText('T1T2'); - - this.runTask(() => { - set(this.context, 'cond1', { isTruthy: true }); - set(this.context, 'cond2', { isTruthy: false }); - }); - - this.assertText('T1F2'); - } - - ['@test it tests for `isTruthy` on Ember objects if available']() { - this.renderValues( - EmberObject.create({ isTruthy: true }), - EmberObject.create({ isTruthy: false }) - ); - - this.assertText('T1F2'); - - this.runTask(() => this.rerender()); - - this.assertText('T1F2'); - - this.runTask(() => set(this.context, 'cond1.isTruthy', false)); - - this.assertText('F1F2'); - - this.runTask(() => { - set(this.context, 'cond1.isTruthy', true); - set(this.context, 'cond2.isTruthy', true); - }); - - this.assertText('T1T2'); - - this.runTask(() => { - set(this.context, 'cond1', EmberObject.create({ isTruthy: true })); - set(this.context, 'cond2', EmberObject.create({ isTruthy: false })); - }); - - this.assertText('T1F2'); - } - - ['@test it considers empty arrays falsy']() { - this.renderValues( - emberA(['hello']), - emberA() - ); - - this.assertText('T1F2'); - - this.runTask(() => this.rerender()); - - this.assertText('T1F2'); - - this.runTask(() => get(this.context, 'cond1').removeAt(0)); - - this.assertText('F1F2'); - - this.runTask(() => { - get(this.context, 'cond1').pushObject('hello'); - get(this.context, 'cond2').pushObjects([1, 2, 3]); - }); - - this.assertText('T1T2'); - - this.runTask(() => { - set(this.context, 'cond1', emberA(['hello'])); - set(this.context, 'cond2', emberA()); - }); - - this.assertText('T1F2'); - } - - ['@test it considers object proxies without content falsy']() { - this.renderValues( - ObjectProxy.create({ content: {} }), - ObjectProxy.create({ content: EmberObject.create() }), - ObjectProxy.create({ content: null }) - ); - - this.assertText('T1T2F3'); - - this.runTask(() => this.rerender()); - - this.assertText('T1T2F3'); - - this.runTask(() => { - set(this.context, 'cond1.content', null); - set(this.context, 'cond2.content', null); - }); - - this.assertText('F1F2F3'); - - this.runTask(() => { - set(this.context, 'cond1.content', EmberObject.create()); - set(this.context, 'cond2.content', {}); - set(this.context, 'cond3.content', { foo: 'bar' }); - }); - - this.assertText('T1T2T3'); - - this.runTask(() => { - set(this.context, 'cond1', ObjectProxy.create({ content: {} })); - set(this.context, 'cond2', ObjectProxy.create({ content: EmberObject.create() })); - set(this.context, 'cond3', ObjectProxy.create({ content: null })); - }); - - this.assertText('T1T2F3'); - } - - ['@test it considers array proxies without content falsy']() { - this.renderValues( - ArrayProxy.create({ content: emberA(['hello']) }), - ArrayProxy.create({ content: null }) - ); - - this.assertText('T1F2'); - - this.runTask(() => this.rerender()); - - this.assertText('T1F2'); - - this.runTask(() => { - set(this.context, 'cond1.content', null); - set(this.context, 'cond2.content', null); - }); - - this.assertText('F1F2'); - - this.runTask(() => { - set(this.context, 'cond1.content', emberA(['hello'])); - set(this.context, 'cond2.content', emberA([1, 2, 3])); - }); - - this.assertText('T1T2'); - - this.runTask(() => { - set(this.context, 'cond1', ArrayProxy.create({ content: emberA(['hello']) })); - set(this.context, 'cond2', ArrayProxy.create({ content: null })); - }); - - this.assertText('T1F2'); - } - - ['@test it considers array proxies with empty arrays falsy']() { - this.renderValues( - ArrayProxy.create({ content: emberA(['hello']) }), - ArrayProxy.create({ content: emberA() }) - ); - - this.assertText('T1F2'); - - this.runTask(() => this.rerender()); - - this.assertText('T1F2'); - - this.runTask(() => get(this.context, 'cond1.content').removeAt(0)); - - this.assertText('F1F2'); - - this.runTask(() => { - get(this.context, 'cond1.content').pushObject('hello'); - get(this.context, 'cond2.content').pushObjects([1, 2, 3]); - }); - - this.assertText('T1T2'); - - this.runTask(() => { - set(this.context, 'cond1', ArrayProxy.create({ content: emberA(['hello']) })); - set(this.context, 'cond2', ArrayProxy.create({ content: emberA() })); - }); - - this.assertText('T1F2'); - } - - ['@htmlbars it does not update when the unbound helper is used']() { - let template = `${ - this.templateFor({ cond: '(unbound cond1)', truthy: 'T1', falsy: 'F1' }) - }${ - this.templateFor({ cond: '(unbound cond2)', truthy: 'T2', falsy: 'F2' }) - }`; - - this.render(template, { cond1: true, cond2: false }); - - this.assertText('T1F2'); - - this.runTask(() => this.rerender()); - - this.assertText('T1F2'); - - this.runTask(() => set(this.context, 'cond1', false)); - - this.assertText('T1F2'); - - this.runTask(() => { - set(this.context, 'cond1', true); - set(this.context, 'cond2', true); - }); - - this.assertText('T1F2'); - - this.runTask(() => { - set(this.context, 'cond1', true); - set(this.context, 'cond2', false); - }); - - this.assertText('T1F2'); - } - - ['@test it maintains DOM stability when condition changes from a truthy to a different truthy value']() { - this.renderValues(true); - - this.assertText('T1'); - - this.takeSnapshot(); - - this.runTask(() => set(this.context, 'cond1', 'hello')); - - this.assertText('T1'); - - this.assertInvariants(); - } - - ['@test it maintains DOM stability when condition changes from a falsy to a different falsy value']() { - this.renderValues(false); - - this.assertText('F1'); - - this.takeSnapshot(); - - this.runTask(() => set(this.context, 'cond1', '')); - - this.assertText('F1'); - - this.assertInvariants(); - } - -} +import { + BASIC_TRUTHY_TESTS, + BASIC_FALSY_TESTS, + SharedConditionalsTest +} from './shared-conditional-tests'; moduleFor('Syntax test: {{#if}}', class extends SharedConditionalsTest { diff --git a/packages/ember-glimmer/tests/integration/syntax/shared-conditional-tests.js b/packages/ember-glimmer/tests/integration/syntax/shared-conditional-tests.js new file mode 100644 index 00000000000..c4713111019 --- /dev/null +++ b/packages/ember-glimmer/tests/integration/syntax/shared-conditional-tests.js @@ -0,0 +1,436 @@ +import { RenderingTest } from '../../utils/test-case'; +import { get } from 'ember-metal/property_get'; +import { set } from 'ember-metal/property_set'; +import assign from 'ember-metal/assign'; +import EmberObject from 'ember-runtime/system/object'; +import ObjectProxy from 'ember-runtime/system/object_proxy'; +import { A as emberA } from 'ember-runtime/system/native_array'; +import ArrayProxy from 'ember-runtime/system/array_proxy'; + +class AbstractConditionalsTest extends RenderingTest { + + /* abstract */ + templateFor({ cond, truthy, falsy }) { + // e.g. `{{#if ${cond}}}${truthy}{{else}}${falsy}{{/if}}` + throw new Error('Not implemented: `templateFor`'); + } + + renderValues(...values) { + let templates = []; + let context = {}; + + for (let i = 1; i <= values.length; i++) { + templates.push(this.templateFor({ cond: `cond${i}`, truthy: `{{t}}${i}`, falsy: `{{f}}${i}` })); + context[`cond${i}`] = values[i - 1]; + } + + this.render(templates.join(''), assign({ t: 'T', f: 'F' }, context)); + } + +} + +/* + The test cases in this file generally follow the following pattern: + + 1. Render with [ truthy, ...(other truthy variations), falsy, ...(other falsy variations) ] + 2. No-op rerender + 3. Make all of them falsy (through interior mutation) + 4. Make all of them truthy (through interior mutation, sometimes with some slight variations) + 5. Reset them to their original values (through replacement) +*/ + +export const BASIC_TRUTHY_TESTS = { + + cases: [ + true, + ' ', + 'hello', + 'false', + 'null', + 'undefined', + 1, + ['hello'], + emberA(['hello']), + {}, + { foo: 'bar' }, + EmberObject.create(), + EmberObject.create({ foo: 'bar' }), + /*jshint -W053 */ + new String('hello'), + new String(''), + new Boolean(true), + new Boolean(false), + new Date() + /*jshint +W053 */ + ], + + generate(value) { + return { + + [`@test it should consider ${JSON.stringify(value)} truthy`]() { + this.renderValues(value); + + this.assertText('T1'); + + this.runTask(() => this.rerender()); + + this.assertText('T1'); + + this.runTask(() => set(this.context, 'cond1', false)); + + this.assertText('F1'); + + this.runTask(() => set(this.context, 'cond1', value)); + + this.assertText('T1'); + } + + }; + } + +}; + +export const BASIC_FALSY_TESTS = { + + cases: [ + false, + null, + undefined, + '', + 0, + [], + emberA() + ], + + generate(value) { + let tests = { + + [`@test it should consider ${JSON.stringify(value)} falsy`]() { + this.renderValues(value); + + this.assertText('F1'); + + this.runTask(() => this.rerender()); + + this.assertText('F1'); + + this.runTask(() => set(this.context, 'cond1', true)); + + this.assertText('T1'); + + this.runTask(() => set(this.context, 'cond1', value)); + + this.assertText('F1'); + } + + }; + + if (value !== false) { + // Only `{ isTruthy: false }` is falsy, `{ isTruthy: null }` etc are not + + tests[`@test it should consider { isTruthy: ${JSON.stringify(value)} } truthy`] = function() { + this.renderValues({ isTruthy: value }); + + this.assertText('T1'); + + this.runTask(() => this.rerender()); + + this.assertText('T1'); + + this.runTask(() => set(this.context, 'cond1.isTruthy', false)); + + this.assertText('F1'); + + this.runTask(() => set(this.context, 'cond1', { isTruthy: value })); + + this.assertText('T1'); + }; + } + } + +}; + +export class SharedConditionalsTest extends AbstractConditionalsTest { + + ['@test it renders the corresponding block based on the conditional']() { + this.renderValues(true, false); + + this.assertText('T1F2'); + + this.runTask(() => this.rerender()); + + this.assertText('T1F2'); + + this.runTask(() => set(this.context, 'cond1', false)); + + this.assertText('F1F2'); + + this.runTask(() => { + set(this.context, 'cond1', true); + set(this.context, 'cond2', true); + }); + + this.assertText('T1T2'); + + this.runTask(() => { + set(this.context, 'cond1', true); + set(this.context, 'cond2', false); + }); + + this.assertText('T1F2'); + } + + ['@test it tests for `isTruthy` if available']() { + this.renderValues({ isTruthy: true }, { isTruthy: false }); + + this.assertText('T1F2'); + + this.runTask(() => this.rerender()); + + this.assertText('T1F2'); + + this.runTask(() => set(this.context, 'cond1.isTruthy', false)); + + this.assertText('F1F2'); + + this.runTask(() => { + set(this.context, 'cond1.isTruthy', true); + set(this.context, 'cond2.isTruthy', true); + }); + + this.assertText('T1T2'); + + this.runTask(() => { + set(this.context, 'cond1', { isTruthy: true }); + set(this.context, 'cond2', { isTruthy: false }); + }); + + this.assertText('T1F2'); + } + + ['@test it tests for `isTruthy` on Ember objects if available']() { + this.renderValues( + EmberObject.create({ isTruthy: true }), + EmberObject.create({ isTruthy: false }) + ); + + this.assertText('T1F2'); + + this.runTask(() => this.rerender()); + + this.assertText('T1F2'); + + this.runTask(() => set(this.context, 'cond1.isTruthy', false)); + + this.assertText('F1F2'); + + this.runTask(() => { + set(this.context, 'cond1.isTruthy', true); + set(this.context, 'cond2.isTruthy', true); + }); + + this.assertText('T1T2'); + + this.runTask(() => { + set(this.context, 'cond1', EmberObject.create({ isTruthy: true })); + set(this.context, 'cond2', EmberObject.create({ isTruthy: false })); + }); + + this.assertText('T1F2'); + } + + ['@test it considers empty arrays falsy']() { + this.renderValues( + emberA(['hello']), + emberA() + ); + + this.assertText('T1F2'); + + this.runTask(() => this.rerender()); + + this.assertText('T1F2'); + + this.runTask(() => get(this.context, 'cond1').removeAt(0)); + + this.assertText('F1F2'); + + this.runTask(() => { + get(this.context, 'cond1').pushObject('hello'); + get(this.context, 'cond2').pushObjects([1, 2, 3]); + }); + + this.assertText('T1T2'); + + this.runTask(() => { + set(this.context, 'cond1', emberA(['hello'])); + set(this.context, 'cond2', emberA()); + }); + + this.assertText('T1F2'); + } + + ['@test it considers object proxies without content falsy']() { + this.renderValues( + ObjectProxy.create({ content: {} }), + ObjectProxy.create({ content: EmberObject.create() }), + ObjectProxy.create({ content: null }) + ); + + this.assertText('T1T2F3'); + + this.runTask(() => this.rerender()); + + this.assertText('T1T2F3'); + + this.runTask(() => { + set(this.context, 'cond1.content', null); + set(this.context, 'cond2.content', null); + }); + + this.assertText('F1F2F3'); + + this.runTask(() => { + set(this.context, 'cond1.content', EmberObject.create()); + set(this.context, 'cond2.content', {}); + set(this.context, 'cond3.content', { foo: 'bar' }); + }); + + this.assertText('T1T2T3'); + + this.runTask(() => { + set(this.context, 'cond1', ObjectProxy.create({ content: {} })); + set(this.context, 'cond2', ObjectProxy.create({ content: EmberObject.create() })); + set(this.context, 'cond3', ObjectProxy.create({ content: null })); + }); + + this.assertText('T1T2F3'); + } + + ['@test it considers array proxies without content falsy']() { + this.renderValues( + ArrayProxy.create({ content: emberA(['hello']) }), + ArrayProxy.create({ content: null }) + ); + + this.assertText('T1F2'); + + this.runTask(() => this.rerender()); + + this.assertText('T1F2'); + + this.runTask(() => { + set(this.context, 'cond1.content', null); + set(this.context, 'cond2.content', null); + }); + + this.assertText('F1F2'); + + this.runTask(() => { + set(this.context, 'cond1.content', emberA(['hello'])); + set(this.context, 'cond2.content', emberA([1, 2, 3])); + }); + + this.assertText('T1T2'); + + this.runTask(() => { + set(this.context, 'cond1', ArrayProxy.create({ content: emberA(['hello']) })); + set(this.context, 'cond2', ArrayProxy.create({ content: null })); + }); + + this.assertText('T1F2'); + } + + ['@test it considers array proxies with empty arrays falsy']() { + this.renderValues( + ArrayProxy.create({ content: emberA(['hello']) }), + ArrayProxy.create({ content: emberA() }) + ); + + this.assertText('T1F2'); + + this.runTask(() => this.rerender()); + + this.assertText('T1F2'); + + this.runTask(() => get(this.context, 'cond1.content').removeAt(0)); + + this.assertText('F1F2'); + + this.runTask(() => { + get(this.context, 'cond1.content').pushObject('hello'); + get(this.context, 'cond2.content').pushObjects([1, 2, 3]); + }); + + this.assertText('T1T2'); + + this.runTask(() => { + set(this.context, 'cond1', ArrayProxy.create({ content: emberA(['hello']) })); + set(this.context, 'cond2', ArrayProxy.create({ content: emberA() })); + }); + + this.assertText('T1F2'); + } + + ['@htmlbars it does not update when the unbound helper is used']() { + let template = `${ + this.templateFor({ cond: '(unbound cond1)', truthy: 'T1', falsy: 'F1' }) + }${ + this.templateFor({ cond: '(unbound cond2)', truthy: 'T2', falsy: 'F2' }) + }`; + + this.render(template, { cond1: true, cond2: false }); + + this.assertText('T1F2'); + + this.runTask(() => this.rerender()); + + this.assertText('T1F2'); + + this.runTask(() => set(this.context, 'cond1', false)); + + this.assertText('T1F2'); + + this.runTask(() => { + set(this.context, 'cond1', true); + set(this.context, 'cond2', true); + }); + + this.assertText('T1F2'); + + this.runTask(() => { + set(this.context, 'cond1', true); + set(this.context, 'cond2', false); + }); + + this.assertText('T1F2'); + } + + ['@test it maintains DOM stability when condition changes from a truthy to a different truthy value']() { + this.renderValues(true); + + this.assertText('T1'); + + this.takeSnapshot(); + + this.runTask(() => set(this.context, 'cond1', 'hello')); + + this.assertText('T1'); + + this.assertInvariants(); + } + + ['@test it maintains DOM stability when condition changes from a falsy to a different falsy value']() { + this.renderValues(false); + + this.assertText('F1'); + + this.takeSnapshot(); + + this.runTask(() => set(this.context, 'cond1', '')); + + this.assertText('F1'); + + this.assertInvariants(); + } +} diff --git a/packages/ember-glimmer/tests/integration/syntax/with-test.js b/packages/ember-glimmer/tests/integration/syntax/with-test.js new file mode 100644 index 00000000000..e2c1a9f5427 --- /dev/null +++ b/packages/ember-glimmer/tests/integration/syntax/with-test.js @@ -0,0 +1,33 @@ +import { moduleFor } from '../../utils/test-case'; +import { set } from 'ember-metal/property_set'; +import { + BASIC_TRUTHY_TESTS, + BASIC_FALSY_TESTS, + SharedConditionalsTest +} from './shared-conditional-tests'; + +moduleFor('Syntax test: {{#with}}', class extends SharedConditionalsTest { + templateFor({ cond, truthy, falsy }) { + return `{{#with ${cond} as |test|}}${truthy}{{else}}${falsy}{{/with}}`; + } + + ['@test it aliases the condition into the block param']() { + 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', false)); + + this.assertText('False'); + + this.runTask(() => this.rerender()); + + this.assertText('False'); + } +}, BASIC_TRUTHY_TESTS, BASIC_FALSY_TESTS);